shvcli 0.6.0__tar.gz → 0.6.2__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.
- {shvcli-0.6.0/shvcli.egg-info → shvcli-0.6.2}/PKG-INFO +15 -12
- {shvcli-0.6.0 → shvcli-0.6.2}/README.rst +12 -9
- {shvcli-0.6.0 → shvcli-0.6.2}/pyproject.toml +2 -4
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/builtin_impl.py +50 -27
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/cli.py +23 -15
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/client.py +3 -3
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/config.py +25 -5
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/lsdir.py +12 -4
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/parse.py +1 -9
- shvcli-0.6.2/shvcli/plugins.py +18 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/tools.py +1 -1
- {shvcli-0.6.0 → shvcli-0.6.2/shvcli.egg-info}/PKG-INFO +15 -12
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli.egg-info/SOURCES.txt +1 -1
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli.egg-info/requires.txt +1 -1
- shvcli-0.6.0/shvcli/version +0 -1
- {shvcli-0.6.0 → shvcli-0.6.2}/LICENSE +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/setup.cfg +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/__init__.py +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/__main__.py +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/__version__.py +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/builtin.py +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/complet.py +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/complet_tools.py +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/py.typed +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/scan.py +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli/valid.py +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli.egg-info/dependency_links.txt +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli.egg-info/entry_points.txt +0 -0
- {shvcli-0.6.0 → shvcli-0.6.2}/shvcli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: shvcli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Silicon Heaven CLI access application
|
|
5
5
|
Author: Elektroline a.s.
|
|
6
6
|
License: MIT License
|
|
@@ -38,7 +38,7 @@ Classifier: Topic :: Terminals
|
|
|
38
38
|
Requires-Python: >=3.11
|
|
39
39
|
Description-Content-Type: text/x-rst
|
|
40
40
|
License-File: LICENSE
|
|
41
|
-
Requires-Dist: pyshv>=0.
|
|
41
|
+
Requires-Dist: pyshv>=0.8.0
|
|
42
42
|
Requires-Dist: prompt_toolkit
|
|
43
43
|
Requires-Dist: pygments
|
|
44
44
|
Requires-Dist: pyxdg
|
|
@@ -140,6 +140,12 @@ switched in runtime. The following options are available:
|
|
|
140
140
|
* **cache**: Controls if cache is preserved between invocations. Cache is
|
|
141
141
|
separated based on the connection URL and stored in files in
|
|
142
142
|
``$XDG_CACHE_HOME/shvcli``. The default is ``true``.
|
|
143
|
+
* **call_attempts**: Number of attempts before method call is abandoned and
|
|
144
|
+
timeout is reported. This makes the total time multiple of **call_timeout**.
|
|
145
|
+
* **call_timeout**: Timeout in seconds before call attempt is abandoned. Based
|
|
146
|
+
on the **call_attempts** call is attempted again or timeout is reported.
|
|
147
|
+
* **autoget_timeout**: Timeout in seconds before call that is part of autoget
|
|
148
|
+
functionality.
|
|
143
149
|
|
|
144
150
|
|
|
145
151
|
Example configuration file:
|
|
@@ -166,10 +172,7 @@ environment you are running in.
|
|
|
166
172
|
|
|
167
173
|
**subscribe|sub**: Add new subscribe. Shortcut to the call of
|
|
168
174
|
``.app/broker/currentClient:subscribe`` that accepts arguments in more convenient
|
|
169
|
-
way
|
|
170
|
-
same format such as method calls in this tool, that means ``PATH:METHOD`` where
|
|
171
|
-
``METHOD`` can be left out to match all methods. Pattern subscribes are not
|
|
172
|
-
supporter, yet.
|
|
175
|
+
way. The argument can be multiple RPC RIs (``PATH:METHOD:SIGNAL`` patterns).
|
|
173
176
|
|
|
174
177
|
**unsubscribe|usub**: Unsubscribe existing subscription. It is reverse operation
|
|
175
178
|
to the **subscribe** and same remarks apply here as well. It is a shortcut to
|
|
@@ -191,11 +194,11 @@ cache.
|
|
|
191
194
|
thus use it sparely.
|
|
192
195
|
|
|
193
196
|
**set|s**: allows modification of configuration option in runtime. The names
|
|
194
|
-
are the same as in ``config`` section. The boolean options
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
also
|
|
198
|
-
simply prints the current configuration.
|
|
197
|
+
are the same as in ``config`` section. The boolean options are set if no
|
|
198
|
+
argument is provided, or cleared if name is prefixed with ``no`` (and thus to
|
|
199
|
+
disable ``raw`` you use ``noraw``). You can also add ``=true`` or ``=false``.
|
|
200
|
+
The equal sign is also used with integer and floating point options. Without any
|
|
201
|
+
configuration option it simply prints the current configuration.
|
|
199
202
|
|
|
200
203
|
|
|
201
204
|
Special methods ``ls`` and ``dir``
|
|
@@ -95,6 +95,12 @@ switched in runtime. The following options are available:
|
|
|
95
95
|
* **cache**: Controls if cache is preserved between invocations. Cache is
|
|
96
96
|
separated based on the connection URL and stored in files in
|
|
97
97
|
``$XDG_CACHE_HOME/shvcli``. The default is ``true``.
|
|
98
|
+
* **call_attempts**: Number of attempts before method call is abandoned and
|
|
99
|
+
timeout is reported. This makes the total time multiple of **call_timeout**.
|
|
100
|
+
* **call_timeout**: Timeout in seconds before call attempt is abandoned. Based
|
|
101
|
+
on the **call_attempts** call is attempted again or timeout is reported.
|
|
102
|
+
* **autoget_timeout**: Timeout in seconds before call that is part of autoget
|
|
103
|
+
functionality.
|
|
98
104
|
|
|
99
105
|
|
|
100
106
|
Example configuration file:
|
|
@@ -121,10 +127,7 @@ environment you are running in.
|
|
|
121
127
|
|
|
122
128
|
**subscribe|sub**: Add new subscribe. Shortcut to the call of
|
|
123
129
|
``.app/broker/currentClient:subscribe`` that accepts arguments in more convenient
|
|
124
|
-
way
|
|
125
|
-
same format such as method calls in this tool, that means ``PATH:METHOD`` where
|
|
126
|
-
``METHOD`` can be left out to match all methods. Pattern subscribes are not
|
|
127
|
-
supporter, yet.
|
|
130
|
+
way. The argument can be multiple RPC RIs (``PATH:METHOD:SIGNAL`` patterns).
|
|
128
131
|
|
|
129
132
|
**unsubscribe|usub**: Unsubscribe existing subscription. It is reverse operation
|
|
130
133
|
to the **subscribe** and same remarks apply here as well. It is a shortcut to
|
|
@@ -146,11 +149,11 @@ cache.
|
|
|
146
149
|
thus use it sparely.
|
|
147
150
|
|
|
148
151
|
**set|s**: allows modification of configuration option in runtime. The names
|
|
149
|
-
are the same as in ``config`` section. The boolean options
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
also
|
|
153
|
-
simply prints the current configuration.
|
|
152
|
+
are the same as in ``config`` section. The boolean options are set if no
|
|
153
|
+
argument is provided, or cleared if name is prefixed with ``no`` (and thus to
|
|
154
|
+
disable ``raw`` you use ``noraw``). You can also add ``=true`` or ``=false``.
|
|
155
|
+
The equal sign is also used with integer and floating point options. Without any
|
|
156
|
+
configuration option it simply prints the current configuration.
|
|
154
157
|
|
|
155
158
|
|
|
156
159
|
Special methods ``ls`` and ``dir``
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "shvcli"
|
|
3
|
-
version = "0.6.
|
|
3
|
+
version = "0.6.2"
|
|
4
4
|
description = "Silicon Heaven CLI access application"
|
|
5
5
|
readme = "README.rst"
|
|
6
6
|
license = {file = "LICENSE"}
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
]
|
|
21
21
|
requires-python = ">=3.11"
|
|
22
22
|
dependencies = [
|
|
23
|
-
"pyshv >= 0.
|
|
23
|
+
"pyshv >= 0.8.0",
|
|
24
24
|
"prompt_toolkit",
|
|
25
25
|
"pygments",
|
|
26
26
|
"pyxdg",
|
|
@@ -77,8 +77,6 @@ ignore = [
|
|
|
77
77
|
"PLR0916",
|
|
78
78
|
# This seems like a wrong rule
|
|
79
79
|
"PLW1641",
|
|
80
|
-
# Deprecated
|
|
81
|
-
"ANN101", "ANN102",
|
|
82
80
|
]
|
|
83
81
|
preview = true
|
|
84
82
|
[tool.ruff.lint.per-file-ignores]
|
|
@@ -33,19 +33,27 @@ def argument_signal_comp(
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
def argument_set_comp(
|
|
36
|
-
|
|
36
|
+
client: SHVClient, config: CliConfig, items: CliItems
|
|
37
37
|
) -> collections.abc.Iterable[Completion]:
|
|
38
38
|
"""Completion for !set method."""
|
|
39
|
-
|
|
40
|
-
if
|
|
41
|
-
|
|
39
|
+
item = items.param_raw.split()[-1] if items.param_raw else ""
|
|
40
|
+
if "=" in item:
|
|
41
|
+
opt, _, val = item.partition("=")
|
|
42
|
+
if config.OPTS.get(opt) == config.Type.BOOL:
|
|
42
43
|
yield from comp_from(val, {"true", "false"})
|
|
43
44
|
else:
|
|
44
|
-
yield from comp_from(items.param_raw, config.OPTS.keys())
|
|
45
45
|
yield from comp_from(
|
|
46
|
-
|
|
46
|
+
item,
|
|
47
|
+
(v for v, t in config.OPTS.items() if t is config.Type.BOOL),
|
|
48
|
+
)
|
|
49
|
+
yield from comp_from(
|
|
50
|
+
item,
|
|
47
51
|
(f"no{v}" for v, t in config.OPTS.items() if t is config.Type.BOOL),
|
|
48
52
|
)
|
|
53
|
+
yield from comp_from(
|
|
54
|
+
item,
|
|
55
|
+
(f"{v}=" for v, t in config.OPTS.items() if t is not config.Type.BOOL),
|
|
56
|
+
)
|
|
49
57
|
|
|
50
58
|
|
|
51
59
|
argument_signal = Argument("[PATH:SIGNAL]", argument_signal_comp, autoprobe=True)
|
|
@@ -54,7 +62,7 @@ argument_set = Argument("[OPTION [VALUE]]", argument_set_comp)
|
|
|
54
62
|
|
|
55
63
|
|
|
56
64
|
@builtin("help", {"h"}, hidden=True)
|
|
57
|
-
def _help(
|
|
65
|
+
def _help(client: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
58
66
|
"""Print help for all builtin methods."""
|
|
59
67
|
print_row("Available internal methods (all prefixed with '!'):")
|
|
60
68
|
for name, m in METHODS.items():
|
|
@@ -83,7 +91,9 @@ async def unsubscribe(shvclient: SHVClient, config: CliConfig, items: CliItems)
|
|
|
83
91
|
|
|
84
92
|
|
|
85
93
|
@builtin(aliases={"subs", "test"})
|
|
86
|
-
async def subscriptions(
|
|
94
|
+
async def subscriptions(
|
|
95
|
+
shvclient: SHVClient, config: CliConfig, items: CliItems
|
|
96
|
+
) -> None:
|
|
87
97
|
"""List current subscriptions."""
|
|
88
98
|
subs = await shvclient.call(".broker/currentClient", "subscriptions")
|
|
89
99
|
if isinstance(subs, dict):
|
|
@@ -118,7 +128,7 @@ def tree(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
|
118
128
|
|
|
119
129
|
|
|
120
130
|
@builtin(argument=argument_path)
|
|
121
|
-
async def cd(
|
|
131
|
+
async def cd(client: SHVClient, config: CliConfig, itemst: CliItems) -> None:
|
|
122
132
|
"""Change to given path even if it is invalid."""
|
|
123
133
|
|
|
124
134
|
|
|
@@ -134,10 +144,9 @@ async def scan(
|
|
|
134
144
|
|
|
135
145
|
|
|
136
146
|
@builtin("set", aliases={"s"}, argument=argument_set)
|
|
137
|
-
def _set(
|
|
147
|
+
def _set(client: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
138
148
|
"""Set configuration options in runtime."""
|
|
139
|
-
|
|
140
|
-
if not opt:
|
|
149
|
+
if not items.param_raw.strip():
|
|
141
150
|
w = max(len(n) for n in config.OPTS.keys())
|
|
142
151
|
for n, t in config.OPTS.items():
|
|
143
152
|
row = [("", (" " * (w - len(n))) + n + " ")]
|
|
@@ -146,25 +155,39 @@ def _set(_: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
|
146
155
|
v = getattr(config, n)
|
|
147
156
|
assert isinstance(v, bool)
|
|
148
157
|
row.append(("ansigreen" if v else "ansired", str(v).lower()))
|
|
149
|
-
case config.Type.INT:
|
|
158
|
+
case config.Type.INT | config.Type.FLOAT:
|
|
150
159
|
row.append(("", str(getattr(config, n))))
|
|
151
160
|
case _:
|
|
152
161
|
raise NotImplementedError(f"Unimplemented {t!r}")
|
|
153
162
|
print_row(row)
|
|
154
163
|
return
|
|
155
164
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
for opt in items.param_raw.split():
|
|
166
|
+
if "=" in opt:
|
|
167
|
+
opt, _, val = opt.partition("=") # noqa: PLW2901
|
|
168
|
+
if opt not in config.OPTS.keys():
|
|
169
|
+
print(f"Invalid option: {opt}")
|
|
170
|
+
return
|
|
171
|
+
value: bool | int | float
|
|
172
|
+
match config.OPTS[opt]:
|
|
173
|
+
case config.Type.BOOL:
|
|
174
|
+
bools = {"true": True, "t": True, "false": False, "f": False}
|
|
175
|
+
if val not in bools:
|
|
176
|
+
print(f"Invalid value, expected 'true' or 'false': {val}")
|
|
177
|
+
return
|
|
178
|
+
value = bools[val]
|
|
179
|
+
case config.Type.INT:
|
|
180
|
+
value = int(val, 0)
|
|
181
|
+
case config.Type.FLOAT:
|
|
182
|
+
value = float(val)
|
|
183
|
+
case _:
|
|
184
|
+
raise NotImplementedError(f"Unimplemented {t!r}")
|
|
185
|
+
setattr(config, opt, value)
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
if no := opt.startswith("no"):
|
|
189
|
+
opt = opt[2:] # noqa: PLW2901
|
|
190
|
+
if opt not in config.OPTS.keys():
|
|
191
|
+
print(f"Invalid option: {opt}")
|
|
168
192
|
return
|
|
169
|
-
|
|
170
|
-
setattr(config, opt, value)
|
|
193
|
+
setattr(config, opt, not no)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Command line interface."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import contextlib
|
|
4
5
|
import json
|
|
5
6
|
import pathlib
|
|
6
7
|
import re
|
|
@@ -30,13 +31,10 @@ async def _app(config: CliConfig, shvclient: SHVClient) -> None:
|
|
|
30
31
|
with histfile.open("w") as _:
|
|
31
32
|
pass
|
|
32
33
|
|
|
33
|
-
completer = CliCompleter(shvclient, config)
|
|
34
|
-
validator = CliValidator(shvclient, config)
|
|
35
|
-
|
|
36
34
|
session: PromptSession = PromptSession(
|
|
37
35
|
history=FileHistory(str(histfile)),
|
|
38
|
-
completer=
|
|
39
|
-
validator=
|
|
36
|
+
completer=CliCompleter(shvclient, config),
|
|
37
|
+
validator=CliValidator(shvclient, config),
|
|
40
38
|
)
|
|
41
39
|
while True:
|
|
42
40
|
try:
|
|
@@ -52,7 +50,6 @@ async def _app(config: CliConfig, shvclient: SHVClient) -> None:
|
|
|
52
50
|
[prompt_path, ("", "> ")], vi_mode=config.vimode
|
|
53
51
|
)
|
|
54
52
|
except EOFError:
|
|
55
|
-
await shvclient.disconnect()
|
|
56
53
|
return
|
|
57
54
|
await handle_line(shvclient, config, result)
|
|
58
55
|
except KeyboardInterrupt:
|
|
@@ -82,15 +79,20 @@ async def run(config: CliConfig, subscriptions: list[str]) -> None:
|
|
|
82
79
|
if config.initial_scan:
|
|
83
80
|
await scan_nodes(shvclient, "", config.initial_scan_depth)
|
|
84
81
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
app_task = asyncio.create_task(_app(config, shvclient))
|
|
83
|
+
disconnect_task = asyncio.create_task(shvclient.client.wait_disconnect())
|
|
84
|
+
tasks: set[asyncio.Task] = {app_task, disconnect_task}
|
|
85
|
+
await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
|
86
|
+
|
|
87
|
+
if not app_task.done():
|
|
88
|
+
app_task.cancel()
|
|
89
|
+
if disconnect_task.done():
|
|
88
90
|
print("Disconnected.")
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
await
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
else:
|
|
92
|
+
await shvclient.disconnect()
|
|
93
|
+
await disconnect_task
|
|
94
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
95
|
+
await app_task
|
|
94
96
|
|
|
95
97
|
if config.cache:
|
|
96
98
|
cachepath.parent.mkdir(exist_ok=True)
|
|
@@ -117,9 +119,15 @@ async def handle_line(shvclient: SHVClient, config: CliConfig, cmdline: str) ->
|
|
|
117
119
|
else:
|
|
118
120
|
print_cpon(
|
|
119
121
|
await shvclient.call(
|
|
120
|
-
config.shvpath(items.path),
|
|
122
|
+
config.shvpath(items.path),
|
|
123
|
+
items.method,
|
|
124
|
+
param,
|
|
125
|
+
call_attempts=config.call_attempts,
|
|
126
|
+
call_timeout=config.call_timeout,
|
|
121
127
|
)
|
|
122
128
|
)
|
|
129
|
+
except TimeoutError:
|
|
130
|
+
print("Call timed out.")
|
|
123
131
|
except RpcError as exc:
|
|
124
132
|
print(f"{type(exc).__name__}: {exc.message}")
|
|
125
133
|
else:
|
|
@@ -13,8 +13,8 @@ from shv import (
|
|
|
13
13
|
RpcMethodFlags,
|
|
14
14
|
RpcMethodNotFoundError,
|
|
15
15
|
SHVType,
|
|
16
|
-
SimpleClient,
|
|
17
16
|
)
|
|
17
|
+
from shv import SHVClient as _SHVClient
|
|
18
18
|
|
|
19
19
|
from . import VERSION
|
|
20
20
|
from .tools import print_cpon
|
|
@@ -67,7 +67,7 @@ class Node(collections.abc.Mapping[str, "Node"]):
|
|
|
67
67
|
if pnode is not None:
|
|
68
68
|
pnode.nodes.pop(path.name)
|
|
69
69
|
|
|
70
|
-
def get_path(self, path: str | PurePosixPath) ->
|
|
70
|
+
def get_path(self, path: str | PurePosixPath) -> Node | None:
|
|
71
71
|
"""Get node on given path."""
|
|
72
72
|
if isinstance(path, str):
|
|
73
73
|
path = PurePosixPath(path)
|
|
@@ -101,7 +101,7 @@ class Node(collections.abc.Mapping[str, "Node"]):
|
|
|
101
101
|
return res
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
class SHVClient(
|
|
104
|
+
class SHVClient(_SHVClient):
|
|
105
105
|
"""Our client caching SHV Tree and reporting received signals."""
|
|
106
106
|
|
|
107
107
|
APP_NAME = "shvcli"
|
|
@@ -4,6 +4,7 @@ import collections.abc
|
|
|
4
4
|
import configparser
|
|
5
5
|
import enum
|
|
6
6
|
import functools
|
|
7
|
+
import itertools
|
|
7
8
|
import logging
|
|
8
9
|
import pathlib
|
|
9
10
|
import subprocess
|
|
@@ -20,6 +21,7 @@ class CliConfig:
|
|
|
20
21
|
|
|
21
22
|
BOOL = enum.auto()
|
|
22
23
|
INT = enum.auto()
|
|
24
|
+
FLOAT = enum.auto()
|
|
23
25
|
|
|
24
26
|
OPTS: collections.abc.Mapping[str, Type] = {
|
|
25
27
|
"vimode": Type.BOOL,
|
|
@@ -27,12 +29,21 @@ class CliConfig:
|
|
|
27
29
|
"autoprobe": Type.BOOL,
|
|
28
30
|
"raw": Type.BOOL,
|
|
29
31
|
"debug": Type.BOOL,
|
|
32
|
+
"call_attempts": Type.INT,
|
|
33
|
+
"call_timeout": Type.FLOAT,
|
|
34
|
+
"autoget_timeout": Type.FLOAT,
|
|
30
35
|
}
|
|
31
|
-
"""
|
|
36
|
+
"""Options allowed to be set in runtime and from configuration file.
|
|
37
|
+
|
|
32
38
|
You can use :func:`setattr` and :func:`getattr`.
|
|
33
39
|
"""
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
COPTS: collections.abc.Mapping[str, Type] = {
|
|
42
|
+
"cache": Type.BOOL,
|
|
43
|
+
}
|
|
44
|
+
"""Options allowed to be set only from configuration file."""
|
|
45
|
+
|
|
46
|
+
def __init__(self) -> None: # noqa: PLR0915
|
|
36
47
|
"""Initialize the configuration to the default and load config files."""
|
|
37
48
|
self.hosts: dict[str, RpcUrl] = {}
|
|
38
49
|
"""Hosts that can be used instead of URL."""
|
|
@@ -53,6 +64,13 @@ class CliConfig:
|
|
|
53
64
|
"""Interpret ls and dir method calls internally or not."""
|
|
54
65
|
self.cache: bool = True
|
|
55
66
|
"""Preserve cache between executions. Not modifiable in runtime!"""
|
|
67
|
+
self.call_attempts: int = 1
|
|
68
|
+
"""Number of call attempts before call is abandoned."""
|
|
69
|
+
self.call_timeout: float = 5.0
|
|
70
|
+
"""Timeout in seconds before call is abandoned."""
|
|
71
|
+
self.autoget_timeout: float = 1.0
|
|
72
|
+
"""Timeout in seconds for call that is part of autoget feature."""
|
|
73
|
+
|
|
56
74
|
self.initial_scan: bool = False
|
|
57
75
|
"""Perform scan right after connection."""
|
|
58
76
|
self.initial_scan_depth: int = 3
|
|
@@ -74,17 +92,19 @@ class CliConfig:
|
|
|
74
92
|
if self.hosts_shell and self.__url is None:
|
|
75
93
|
self.__url = self.hosts_shell[next(iter(self.hosts_shell))]
|
|
76
94
|
case "config":
|
|
77
|
-
for n, t in self.OPTS.items():
|
|
95
|
+
for n, t in itertools.chain(self.OPTS.items(), self.COPTS.items()):
|
|
78
96
|
value = getattr(self, n)
|
|
79
97
|
match t:
|
|
80
98
|
case self.Type.BOOL:
|
|
81
99
|
value = sec.getboolean(n, fallback=value)
|
|
82
100
|
case self.Type.INT:
|
|
83
|
-
value = sec.
|
|
101
|
+
value = sec.getint(n, fallback=value)
|
|
102
|
+
case self.Type.FLOAT:
|
|
103
|
+
value = sec.getfloat(n, fallback=value)
|
|
84
104
|
case _:
|
|
85
105
|
raise NotImplementedError(f"Unhandled type: {t!r}")
|
|
86
106
|
setattr(self, n, value)
|
|
87
|
-
if opts := set(sec.keys()) - set(self.OPTS.
|
|
107
|
+
if opts := set(sec.keys()) - set(self.OPTS) - set(self.COPTS):
|
|
88
108
|
raise ValueError(
|
|
89
109
|
f"Invalid configuration option: {', '.join(opts)}"
|
|
90
110
|
)
|
|
@@ -85,9 +85,12 @@ async def ls_method(shvclient: SHVClient, config: CliConfig, items: CliItems) ->
|
|
|
85
85
|
if "get" in nv.methods:
|
|
86
86
|
try:
|
|
87
87
|
resp = await shvclient.call(
|
|
88
|
-
str(pathlib.PurePosixPath(shvpath) / nn),
|
|
88
|
+
str(pathlib.PurePosixPath(shvpath) / nn),
|
|
89
|
+
"get",
|
|
90
|
+
call_attempts=1,
|
|
91
|
+
call_timeout=config.autoget_timeout,
|
|
89
92
|
)
|
|
90
|
-
except RpcError:
|
|
93
|
+
except (RpcError, TimeoutError):
|
|
91
94
|
pass
|
|
92
95
|
else:
|
|
93
96
|
print_row(
|
|
@@ -113,8 +116,13 @@ async def dir_method(shvclient: SHVClient, config: CliConfig, items: CliItems) -
|
|
|
113
116
|
n = [("", " " * (w - len(d.name))), dir_method_format(d)]
|
|
114
117
|
if _use_autoget(d):
|
|
115
118
|
try:
|
|
116
|
-
resp = await shvclient.call(
|
|
117
|
-
|
|
119
|
+
resp = await shvclient.call(
|
|
120
|
+
shvpath,
|
|
121
|
+
d.name,
|
|
122
|
+
call_attempts=1,
|
|
123
|
+
call_timeout=config.autoget_timeout,
|
|
124
|
+
)
|
|
125
|
+
except (RpcError, TimeoutError):
|
|
118
126
|
pass
|
|
119
127
|
else:
|
|
120
128
|
print_row(
|
|
@@ -59,17 +59,9 @@ class CliItems:
|
|
|
59
59
|
path = parts[0] if parts[0] else "**"
|
|
60
60
|
method = parts[1] if len(parts) == 2 else "*"
|
|
61
61
|
signal = parts[2] if len(parts) == 3 else "*"
|
|
62
|
-
res.append(f"{self.path}{
|
|
62
|
+
res.append(f"{self.path}{'/' if self.path else ''}{path}:{method}:{signal}")
|
|
63
63
|
return res
|
|
64
64
|
|
|
65
|
-
def interpret_param_set(self) -> tuple[str | None, str | None]:
|
|
66
|
-
"""Interpret parameter as 'set' method specification.
|
|
67
|
-
|
|
68
|
-
:return: First word and rest of the string.
|
|
69
|
-
"""
|
|
70
|
-
s = self.param_raw.split(" ", maxsplit=1)
|
|
71
|
-
return s[0] if s else None, s[1] if len(s) > 1 else None
|
|
72
|
-
|
|
73
65
|
|
|
74
66
|
def parse_line(line: str) -> CliItems:
|
|
75
67
|
"""Parse CLI line."""
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Utility to manage plugins."""
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import importlib.metadata
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def load_plugins() -> None:
|
|
11
|
+
"""Import plugins.
|
|
12
|
+
|
|
13
|
+
Plugins get inserted automatically. We have to only ensure that we import
|
|
14
|
+
them.
|
|
15
|
+
"""
|
|
16
|
+
for plugin in importlib.metadata.entry_points(group="shvcli.plugins"):
|
|
17
|
+
logger.debug("Loading plugin: %s", plugin.name)
|
|
18
|
+
importlib.__import__(plugin.value)
|
|
@@ -141,7 +141,7 @@ def print_cpon(data: shv.SHVType, prefix: str = "", short: bool = False) -> None
|
|
|
141
141
|
cpon = "\n".join(_wrap_cpon(prefix + shv.Cpon.pack(data)))[len(prefix) :]
|
|
142
142
|
print_ftext(itertools.chain(iter((("", prefix),)), cpon_ftext(cpon)))
|
|
143
143
|
else:
|
|
144
|
-
|
|
144
|
+
print_ftext(
|
|
145
145
|
itertools.chain(
|
|
146
146
|
iter((("", prefix),)),
|
|
147
147
|
cpon_ftext(shv.Cpon.pack(data, shv.CponWriter.Options(indent=b" "))),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: shvcli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Silicon Heaven CLI access application
|
|
5
5
|
Author: Elektroline a.s.
|
|
6
6
|
License: MIT License
|
|
@@ -38,7 +38,7 @@ Classifier: Topic :: Terminals
|
|
|
38
38
|
Requires-Python: >=3.11
|
|
39
39
|
Description-Content-Type: text/x-rst
|
|
40
40
|
License-File: LICENSE
|
|
41
|
-
Requires-Dist: pyshv>=0.
|
|
41
|
+
Requires-Dist: pyshv>=0.8.0
|
|
42
42
|
Requires-Dist: prompt_toolkit
|
|
43
43
|
Requires-Dist: pygments
|
|
44
44
|
Requires-Dist: pyxdg
|
|
@@ -140,6 +140,12 @@ switched in runtime. The following options are available:
|
|
|
140
140
|
* **cache**: Controls if cache is preserved between invocations. Cache is
|
|
141
141
|
separated based on the connection URL and stored in files in
|
|
142
142
|
``$XDG_CACHE_HOME/shvcli``. The default is ``true``.
|
|
143
|
+
* **call_attempts**: Number of attempts before method call is abandoned and
|
|
144
|
+
timeout is reported. This makes the total time multiple of **call_timeout**.
|
|
145
|
+
* **call_timeout**: Timeout in seconds before call attempt is abandoned. Based
|
|
146
|
+
on the **call_attempts** call is attempted again or timeout is reported.
|
|
147
|
+
* **autoget_timeout**: Timeout in seconds before call that is part of autoget
|
|
148
|
+
functionality.
|
|
143
149
|
|
|
144
150
|
|
|
145
151
|
Example configuration file:
|
|
@@ -166,10 +172,7 @@ environment you are running in.
|
|
|
166
172
|
|
|
167
173
|
**subscribe|sub**: Add new subscribe. Shortcut to the call of
|
|
168
174
|
``.app/broker/currentClient:subscribe`` that accepts arguments in more convenient
|
|
169
|
-
way
|
|
170
|
-
same format such as method calls in this tool, that means ``PATH:METHOD`` where
|
|
171
|
-
``METHOD`` can be left out to match all methods. Pattern subscribes are not
|
|
172
|
-
supporter, yet.
|
|
175
|
+
way. The argument can be multiple RPC RIs (``PATH:METHOD:SIGNAL`` patterns).
|
|
173
176
|
|
|
174
177
|
**unsubscribe|usub**: Unsubscribe existing subscription. It is reverse operation
|
|
175
178
|
to the **subscribe** and same remarks apply here as well. It is a shortcut to
|
|
@@ -191,11 +194,11 @@ cache.
|
|
|
191
194
|
thus use it sparely.
|
|
192
195
|
|
|
193
196
|
**set|s**: allows modification of configuration option in runtime. The names
|
|
194
|
-
are the same as in ``config`` section. The boolean options
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
also
|
|
198
|
-
simply prints the current configuration.
|
|
197
|
+
are the same as in ``config`` section. The boolean options are set if no
|
|
198
|
+
argument is provided, or cleared if name is prefixed with ``no`` (and thus to
|
|
199
|
+
disable ``raw`` you use ``noraw``). You can also add ``=true`` or ``=false``.
|
|
200
|
+
The equal sign is also used with integer and floating point options. Without any
|
|
201
|
+
configuration option it simply prints the current configuration.
|
|
199
202
|
|
|
200
203
|
|
|
201
204
|
Special methods ``ls`` and ``dir``
|
|
@@ -13,11 +13,11 @@ shvcli/complet_tools.py
|
|
|
13
13
|
shvcli/config.py
|
|
14
14
|
shvcli/lsdir.py
|
|
15
15
|
shvcli/parse.py
|
|
16
|
+
shvcli/plugins.py
|
|
16
17
|
shvcli/py.typed
|
|
17
18
|
shvcli/scan.py
|
|
18
19
|
shvcli/tools.py
|
|
19
20
|
shvcli/valid.py
|
|
20
|
-
shvcli/version
|
|
21
21
|
shvcli.egg-info/PKG-INFO
|
|
22
22
|
shvcli.egg-info/SOURCES.txt
|
|
23
23
|
shvcli.egg-info/dependency_links.txt
|
shvcli-0.6.0/shvcli/version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.3.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|