oaknut-econet-host 13.6.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.
- oaknut_econet_host-13.6.0/LICENSE +21 -0
- oaknut_econet_host-13.6.0/PKG-INFO +49 -0
- oaknut_econet_host-13.6.0/README.md +22 -0
- oaknut_econet_host-13.6.0/pyproject.toml +55 -0
- oaknut_econet_host-13.6.0/setup.cfg +4 -0
- oaknut_econet_host-13.6.0/src/oaknut/econet/host/__init__.py +12 -0
- oaknut_econet_host-13.6.0/src/oaknut/econet/host/cli.py +153 -0
- oaknut_econet_host-13.6.0/src/oaknut_econet_host.egg-info/PKG-INFO +49 -0
- oaknut_econet_host-13.6.0/src/oaknut_econet_host.egg-info/SOURCES.txt +12 -0
- oaknut_econet_host-13.6.0/src/oaknut_econet_host.egg-info/dependency_links.txt +1 -0
- oaknut_econet_host-13.6.0/src/oaknut_econet_host.egg-info/entry_points.txt +2 -0
- oaknut_econet_host-13.6.0/src/oaknut_econet_host.egg-info/requires.txt +5 -0
- oaknut_econet_host-13.6.0/src/oaknut_econet_host.egg-info/top_level.txt +1 -0
- oaknut_econet_host-13.6.0/tests/test_cli.py +66 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robert Smallshire
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oaknut-econet-host
|
|
3
|
+
Version: 13.6.0
|
|
4
|
+
Summary: The econet-host program: a CLI that configures Econet transports and service plug-ins and runs a station.
|
|
5
|
+
Author-email: Robert Smallshire <robert@smallshire.org.uk>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rob-smallshire/oaknut/tree/master/packages/oaknut-econet-host
|
|
8
|
+
Project-URL: Repository, https://github.com/rob-smallshire/oaknut
|
|
9
|
+
Project-URL: Issues, https://github.com/rob-smallshire/oaknut/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: System Administrators
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: System :: Networking
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: oaknut-econet-station>=10.0
|
|
22
|
+
Requires-Dist: oaknut-exception>=10.0
|
|
23
|
+
Requires-Dist: oaknut-extension>=10.0
|
|
24
|
+
Requires-Dist: oaknut-cli>=10.0
|
|
25
|
+
Requires-Dist: click>=8.1.7
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# oaknut-econet-host
|
|
29
|
+
|
|
30
|
+
The **`econet-host`** program — the CLI that turns installed Econet transport
|
|
31
|
+
and service plug-ins into a running station.
|
|
32
|
+
|
|
33
|
+
It depends on the `oaknut-econet-station` library and on Click (a hard
|
|
34
|
+
dependency — this *is* a CLI), and discovers whatever transports
|
|
35
|
+
(`oaknut.econet.transport`) and services (`oaknut.econet.service`) are installed.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
econet-host run [--config PATH] # run the station until SIGINT/SIGTERM
|
|
39
|
+
econet-host validate [--config PATH] # build the config, print the plan, exit
|
|
40
|
+
econet-host list-transports # installed transport plug-ins
|
|
41
|
+
econet-host list-services # installed service plug-ins
|
|
42
|
+
econet-host describe <name> # a plug-in's description
|
|
43
|
+
econet-host run --transport aun --station 0.254 --service kvstore # flag-only quick path
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Configuration is TOML, discovered (first match wins) from `--config`, then
|
|
47
|
+
`[tool.econet-host]` in a `pyproject.toml`, then `econet-host.toml`, else the
|
|
48
|
+
flags above. See `docs/dev/econet-host.md` for the design and the recommended
|
|
49
|
+
uv-project deployment pattern.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# oaknut-econet-host
|
|
2
|
+
|
|
3
|
+
The **`econet-host`** program — the CLI that turns installed Econet transport
|
|
4
|
+
and service plug-ins into a running station.
|
|
5
|
+
|
|
6
|
+
It depends on the `oaknut-econet-station` library and on Click (a hard
|
|
7
|
+
dependency — this *is* a CLI), and discovers whatever transports
|
|
8
|
+
(`oaknut.econet.transport`) and services (`oaknut.econet.service`) are installed.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
econet-host run [--config PATH] # run the station until SIGINT/SIGTERM
|
|
12
|
+
econet-host validate [--config PATH] # build the config, print the plan, exit
|
|
13
|
+
econet-host list-transports # installed transport plug-ins
|
|
14
|
+
econet-host list-services # installed service plug-ins
|
|
15
|
+
econet-host describe <name> # a plug-in's description
|
|
16
|
+
econet-host run --transport aun --station 0.254 --service kvstore # flag-only quick path
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Configuration is TOML, discovered (first match wins) from `--config`, then
|
|
20
|
+
`[tool.econet-host]` in a `pyproject.toml`, then `econet-host.toml`, else the
|
|
21
|
+
flags above. See `docs/dev/econet-host.md` for the design and the recommended
|
|
22
|
+
uv-project deployment pattern.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "oaknut-econet-host"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
authors = [{ name = "Robert Smallshire", email = "robert@smallshire.org.uk" }]
|
|
9
|
+
description = "The econet-host program: a CLI that configures Econet transports and service plug-ins and runs a station."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
requires-python = ">=3.12"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: System Administrators",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Topic :: System :: Networking",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"oaknut-econet-station>=10.0",
|
|
26
|
+
"oaknut-exception>=10.0",
|
|
27
|
+
"oaknut-extension>=10.0",
|
|
28
|
+
"oaknut-cli>=10.0",
|
|
29
|
+
"click>=8.1.7",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
econet-host = "oaknut.econet.host.cli:cli"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/rob-smallshire/oaknut/tree/master/packages/oaknut-econet-host"
|
|
37
|
+
Repository = "https://github.com/rob-smallshire/oaknut"
|
|
38
|
+
Issues = "https://github.com/rob-smallshire/oaknut/issues"
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
test = [
|
|
42
|
+
"pytest>=8.0",
|
|
43
|
+
"pytest-asyncio>=0.24",
|
|
44
|
+
]
|
|
45
|
+
dev = [
|
|
46
|
+
"bump-my-version>=0.28.0",
|
|
47
|
+
"pre-commit>=3.0",
|
|
48
|
+
{include-group = "test"},
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.dynamic]
|
|
52
|
+
version = { attr = "oaknut.econet.host.__version__" }
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.packages.find]
|
|
55
|
+
where = ["src"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""The econet-host host program.
|
|
2
|
+
|
|
3
|
+
A CLI (the ``econet-host`` console script) that configures Econet transports
|
|
4
|
+
and service plug-ins from TOML and runs a :class:`~oaknut.econet.station.Station`.
|
|
5
|
+
The CLI itself lives in :mod:`oaknut.econet.host.cli`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
__version__ = "13.6.0"
|
|
11
|
+
|
|
12
|
+
__all__: list[str] = []
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""The ``econet-host`` host CLI: configure transports + services and run."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import contextlib
|
|
7
|
+
import signal
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
from oaknut.cli import use_plain_help
|
|
12
|
+
from oaknut.econet.host import __version__
|
|
13
|
+
from oaknut.econet.station import Station, build_station, config_from_flags, load_config
|
|
14
|
+
from oaknut.exception import ConfigurationError, handled_errors
|
|
15
|
+
from oaknut.extension import describe_extension, list_extensions, namespace_for
|
|
16
|
+
|
|
17
|
+
_TRANSPORT_NS = namespace_for("econet.transport")
|
|
18
|
+
_SERVICE_NS = namespace_for("econet.service")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _print_error(line: str, is_continuation: bool = False) -> None:
|
|
22
|
+
click.secho(line, err=True, fg="yellow" if is_continuation else "red")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _BoundaryGroup(click.Group):
|
|
26
|
+
"""Wraps every command in the oaknut handled_errors boundary, honouring --debug."""
|
|
27
|
+
|
|
28
|
+
def invoke(self, ctx: click.Context):
|
|
29
|
+
debug = bool(ctx.params.get("debug", False))
|
|
30
|
+
with handled_errors(_print_error, debug=debug):
|
|
31
|
+
return super().invoke(ctx)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _resolve_config(config_path, transport, station, services):
|
|
35
|
+
if config_path is not None:
|
|
36
|
+
return load_config(config_path)
|
|
37
|
+
if transport is not None or station is not None or services:
|
|
38
|
+
if transport is None or station is None:
|
|
39
|
+
raise ConfigurationError("the flag path needs both --transport and --station")
|
|
40
|
+
config = config_from_flags(transport=transport, station=station, services=tuple(services))
|
|
41
|
+
return config, "command-line flags"
|
|
42
|
+
return load_config(None)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@click.group(cls=_BoundaryGroup)
|
|
46
|
+
@click.option("--debug", is_flag=True, help="Show full tracebacks for internal errors.")
|
|
47
|
+
@click.version_option(__version__, prog_name="econet-host")
|
|
48
|
+
def cli(debug: bool) -> None:
|
|
49
|
+
"""Host Econet services on a single station."""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
_CONFIG = click.option(
|
|
53
|
+
"--config",
|
|
54
|
+
"config_path",
|
|
55
|
+
default=None,
|
|
56
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
57
|
+
help="Path to a station config TOML file.",
|
|
58
|
+
)
|
|
59
|
+
_TRANSPORT = click.option("--transport", default=None, help="Transport plug-in (flag-only path).")
|
|
60
|
+
_STATION = click.option("--station", default=None, help="Station address, e.g. 0.254 (flag path).")
|
|
61
|
+
_SERVICE = click.option(
|
|
62
|
+
"--service", "services", multiple=True, help="A service plug-in to host (repeatable)."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@cli.command()
|
|
67
|
+
@_CONFIG
|
|
68
|
+
@_TRANSPORT
|
|
69
|
+
@_STATION
|
|
70
|
+
@_SERVICE
|
|
71
|
+
def run(config_path, transport, station, services) -> None:
|
|
72
|
+
"""Run the station host until interrupted (SIGINT/SIGTERM)."""
|
|
73
|
+
config, source = _resolve_config(config_path, transport, station, services)
|
|
74
|
+
host = build_station(config)
|
|
75
|
+
click.echo(f"econet-host {host.address} on '{config.transport}' (config: {source})")
|
|
76
|
+
_echo_port_map(host)
|
|
77
|
+
_run_blocking(host)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@cli.command()
|
|
81
|
+
@_CONFIG
|
|
82
|
+
@_TRANSPORT
|
|
83
|
+
@_STATION
|
|
84
|
+
@_SERVICE
|
|
85
|
+
def validate(config_path, transport, station, services) -> None:
|
|
86
|
+
"""Parse and build the configuration, print the plan, then exit."""
|
|
87
|
+
config, source = _resolve_config(config_path, transport, station, services)
|
|
88
|
+
host = build_station(config)
|
|
89
|
+
click.echo(f"config source: {source}")
|
|
90
|
+
click.echo(f"station: {host.address}")
|
|
91
|
+
click.echo(f"transport: {config.transport}")
|
|
92
|
+
_echo_port_map(host)
|
|
93
|
+
click.echo("ok")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@cli.command("list-transports")
|
|
97
|
+
def list_transports() -> None:
|
|
98
|
+
"""List the installed transport plug-ins."""
|
|
99
|
+
for name in sorted(list_extensions(_TRANSPORT_NS)):
|
|
100
|
+
click.echo(name)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@cli.command("list-services")
|
|
104
|
+
def list_services() -> None:
|
|
105
|
+
"""List the installed service plug-ins."""
|
|
106
|
+
for name in sorted(list_extensions(_SERVICE_NS)):
|
|
107
|
+
click.echo(name)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@cli.command()
|
|
111
|
+
@click.argument("name")
|
|
112
|
+
def describe(name: str) -> None:
|
|
113
|
+
"""Describe a transport or service plug-in by name."""
|
|
114
|
+
for kind, namespace in (("econet.transport", _TRANSPORT_NS), ("econet.service", _SERVICE_NS)):
|
|
115
|
+
if name in list_extensions(namespace):
|
|
116
|
+
click.echo(describe_extension(kind, namespace, name))
|
|
117
|
+
return
|
|
118
|
+
raise ConfigurationError(f"no transport or service named {name!r}")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _echo_port_map(host: Station) -> None:
|
|
122
|
+
port_map = host.port_map
|
|
123
|
+
if not port_map:
|
|
124
|
+
click.echo(" (no services)")
|
|
125
|
+
return
|
|
126
|
+
for port in sorted(port_map):
|
|
127
|
+
click.echo(f" &{port:02X} {port_map[port]}")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
async def serve_until(host: Station, stop: asyncio.Event) -> None:
|
|
131
|
+
"""Open the transport, serve, and shut down gracefully when *stop* is set."""
|
|
132
|
+
await host.transport.open()
|
|
133
|
+
serve_task = asyncio.create_task(host.serve())
|
|
134
|
+
try:
|
|
135
|
+
await stop.wait()
|
|
136
|
+
finally:
|
|
137
|
+
await host.transport.close()
|
|
138
|
+
await serve_task
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _run_blocking(host: Station) -> None:
|
|
142
|
+
async def _main() -> None:
|
|
143
|
+
stop = asyncio.Event()
|
|
144
|
+
loop = asyncio.get_running_loop()
|
|
145
|
+
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
146
|
+
with contextlib.suppress(NotImplementedError):
|
|
147
|
+
loop.add_signal_handler(sig, stop.set)
|
|
148
|
+
await serve_until(host, stop)
|
|
149
|
+
|
|
150
|
+
asyncio.run(_main())
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
use_plain_help(cli)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oaknut-econet-host
|
|
3
|
+
Version: 13.6.0
|
|
4
|
+
Summary: The econet-host program: a CLI that configures Econet transports and service plug-ins and runs a station.
|
|
5
|
+
Author-email: Robert Smallshire <robert@smallshire.org.uk>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rob-smallshire/oaknut/tree/master/packages/oaknut-econet-host
|
|
8
|
+
Project-URL: Repository, https://github.com/rob-smallshire/oaknut
|
|
9
|
+
Project-URL: Issues, https://github.com/rob-smallshire/oaknut/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: System Administrators
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: System :: Networking
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: oaknut-econet-station>=10.0
|
|
22
|
+
Requires-Dist: oaknut-exception>=10.0
|
|
23
|
+
Requires-Dist: oaknut-extension>=10.0
|
|
24
|
+
Requires-Dist: oaknut-cli>=10.0
|
|
25
|
+
Requires-Dist: click>=8.1.7
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# oaknut-econet-host
|
|
29
|
+
|
|
30
|
+
The **`econet-host`** program — the CLI that turns installed Econet transport
|
|
31
|
+
and service plug-ins into a running station.
|
|
32
|
+
|
|
33
|
+
It depends on the `oaknut-econet-station` library and on Click (a hard
|
|
34
|
+
dependency — this *is* a CLI), and discovers whatever transports
|
|
35
|
+
(`oaknut.econet.transport`) and services (`oaknut.econet.service`) are installed.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
econet-host run [--config PATH] # run the station until SIGINT/SIGTERM
|
|
39
|
+
econet-host validate [--config PATH] # build the config, print the plan, exit
|
|
40
|
+
econet-host list-transports # installed transport plug-ins
|
|
41
|
+
econet-host list-services # installed service plug-ins
|
|
42
|
+
econet-host describe <name> # a plug-in's description
|
|
43
|
+
econet-host run --transport aun --station 0.254 --service kvstore # flag-only quick path
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Configuration is TOML, discovered (first match wins) from `--config`, then
|
|
47
|
+
`[tool.econet-host]` in a `pyproject.toml`, then `econet-host.toml`, else the
|
|
48
|
+
flags above. See `docs/dev/econet-host.md` for the design and the recommended
|
|
49
|
+
uv-project deployment pattern.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/oaknut/econet/host/__init__.py
|
|
5
|
+
src/oaknut/econet/host/cli.py
|
|
6
|
+
src/oaknut_econet_host.egg-info/PKG-INFO
|
|
7
|
+
src/oaknut_econet_host.egg-info/SOURCES.txt
|
|
8
|
+
src/oaknut_econet_host.egg-info/dependency_links.txt
|
|
9
|
+
src/oaknut_econet_host.egg-info/entry_points.txt
|
|
10
|
+
src/oaknut_econet_host.egg-info/requires.txt
|
|
11
|
+
src/oaknut_econet_host.egg-info/top_level.txt
|
|
12
|
+
tests/test_cli.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
oaknut
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Tests for the econet-host host CLI and the serve lifecycle."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from click.testing import CliRunner
|
|
6
|
+
from oaknut.econet.core import Address, TestTransport
|
|
7
|
+
from oaknut.econet.host.cli import cli, serve_until
|
|
8
|
+
from oaknut.econet.station import Station
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_list_transports_lists_installed_plugins():
|
|
12
|
+
result = CliRunner().invoke(cli, ["list-transports"])
|
|
13
|
+
assert result.exit_code == 0, result.output
|
|
14
|
+
assert "aun" in result.output
|
|
15
|
+
assert "piconet" in result.output
|
|
16
|
+
assert "hat" in result.output
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_list_services_lists_kvstore():
|
|
20
|
+
result = CliRunner().invoke(cli, ["list-services"])
|
|
21
|
+
assert result.exit_code == 0, result.output
|
|
22
|
+
assert "kvstore" in result.output
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_describe_a_transport():
|
|
26
|
+
result = CliRunner().invoke(cli, ["describe", "aun"])
|
|
27
|
+
assert result.exit_code == 0, result.output
|
|
28
|
+
assert result.output.strip()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_describe_unknown_name_errors():
|
|
32
|
+
result = CliRunner().invoke(cli, ["describe", "no-such-plugin"])
|
|
33
|
+
assert result.exit_code != 0
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_validate_flag_path():
|
|
37
|
+
result = CliRunner().invoke(
|
|
38
|
+
cli, ["validate", "--transport", "aun", "--station", "0.254", "--service", "kvstore"]
|
|
39
|
+
)
|
|
40
|
+
assert result.exit_code == 0, result.output
|
|
41
|
+
assert "0.254" in result.output
|
|
42
|
+
assert "aun" in result.output
|
|
43
|
+
assert "kvstore" in result.output
|
|
44
|
+
assert "command-line flags" in result.output
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_validate_unknown_transport_errors():
|
|
48
|
+
result = CliRunner().invoke(cli, ["validate", "--transport", "nope", "--station", "0.254"])
|
|
49
|
+
assert result.exit_code != 0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_validate_flag_path_needs_both_transport_and_station():
|
|
53
|
+
result = CliRunner().invoke(cli, ["validate", "--transport", "aun"])
|
|
54
|
+
assert result.exit_code != 0
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def test_serve_until_opens_then_closes_on_stop():
|
|
58
|
+
transport = TestTransport(local_station=Address(0, 254))
|
|
59
|
+
station = Station(transport)
|
|
60
|
+
stop = asyncio.Event()
|
|
61
|
+
task = asyncio.create_task(serve_until(station, stop))
|
|
62
|
+
await asyncio.sleep(0)
|
|
63
|
+
assert transport.is_open
|
|
64
|
+
stop.set()
|
|
65
|
+
await asyncio.wait_for(task, timeout=1.0)
|
|
66
|
+
assert transport.is_closed
|