shvcli 0.4.0__tar.gz → 0.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.
- {shvcli-0.4.0/shvcli.egg-info → shvcli-0.6.0}/PKG-INFO +2 -2
- {shvcli-0.4.0 → shvcli-0.6.0}/pyproject.toml +3 -3
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/__main__.py +7 -1
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/__version__.py +5 -4
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/builtin.py +7 -2
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/builtin_impl.py +21 -13
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/cli.py +4 -2
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/client.py +14 -17
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/complet.py +2 -1
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/config.py +2 -2
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/lsdir.py +19 -1
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/parse.py +12 -17
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/tools.py +27 -11
- {shvcli-0.4.0 → shvcli-0.6.0/shvcli.egg-info}/PKG-INFO +2 -2
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/requires.txt +1 -1
- {shvcli-0.4.0 → shvcli-0.6.0}/LICENSE +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/README.rst +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/setup.cfg +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/__init__.py +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/complet_tools.py +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/py.typed +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/scan.py +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/valid.py +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/version +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/SOURCES.txt +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/dependency_links.txt +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/entry_points.txt +0 -0
- {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: shvcli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
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.7.1
|
|
42
42
|
Requires-Dist: prompt_toolkit
|
|
43
43
|
Requires-Dist: pygments
|
|
44
44
|
Requires-Dist: pyxdg
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "shvcli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
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.7.1",
|
|
24
24
|
"prompt_toolkit",
|
|
25
25
|
"pygments",
|
|
26
26
|
"pyxdg",
|
|
@@ -52,7 +52,7 @@ exclude_lines = [
|
|
|
52
52
|
target-version = "py311"
|
|
53
53
|
[tool.ruff.lint]
|
|
54
54
|
select = [
|
|
55
|
-
"F", "E", "W", "I", "N", "D", "UP", "YTT", "ANN", "ASYNC", "
|
|
55
|
+
"F", "E", "W", "I", "N", "D", "UP", "YTT", "ANN", "ASYNC", "ASYNC1", "S", "BLE",
|
|
56
56
|
"B", "A", "COM", "PL", "RUF"
|
|
57
57
|
]
|
|
58
58
|
ignore = [
|
|
@@ -21,6 +21,12 @@ def parse_args() -> argparse.Namespace:
|
|
|
21
21
|
)
|
|
22
22
|
parser.add_argument(
|
|
23
23
|
"-s",
|
|
24
|
+
"--subscribe",
|
|
25
|
+
action="append",
|
|
26
|
+
default=[],
|
|
27
|
+
help="Automatic subscription right after connection.",
|
|
28
|
+
)
|
|
29
|
+
parser.add_argument(
|
|
24
30
|
"--scan",
|
|
25
31
|
action="store_true",
|
|
26
32
|
help="Perform scan as a first action right after connect.",
|
|
@@ -49,7 +55,7 @@ def main() -> None:
|
|
|
49
55
|
config.debug = config.debug or args.debug
|
|
50
56
|
config.initial_scan = args.scan
|
|
51
57
|
config.initial_scan_depth = args.scan_depth
|
|
52
|
-
asyncio.run(run(config))
|
|
58
|
+
asyncio.run(run(config, args.subscribe))
|
|
53
59
|
|
|
54
60
|
|
|
55
61
|
if __name__ == "__main__":
|
|
@@ -15,10 +15,11 @@ def _get_version() -> str:
|
|
|
15
15
|
if str(dist.locate_file(f"{__package__}/__version__.py")) == __file__:
|
|
16
16
|
return dist.version
|
|
17
17
|
pyproject = pathlib.Path(__file__).parent.parent / "pyproject.toml"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
if pyproject.exists():
|
|
19
|
+
with pyproject.open("rb") as f:
|
|
20
|
+
res = tomllib.load(f)["project"]["version"]
|
|
21
|
+
if isinstance(res, str):
|
|
22
|
+
return res
|
|
22
23
|
return "unknown"
|
|
23
24
|
|
|
24
25
|
|
|
@@ -10,7 +10,9 @@ from .client import SHVClient
|
|
|
10
10
|
from .config import CliConfig
|
|
11
11
|
from .parse import CliItems
|
|
12
12
|
|
|
13
|
-
_T_METHOD = typing.Callable[
|
|
13
|
+
_T_METHOD = typing.Callable[
|
|
14
|
+
[SHVClient, CliConfig, CliItems], typing.Awaitable[None] | None
|
|
15
|
+
]
|
|
14
16
|
_T_XMETHOD = typing.Callable[
|
|
15
17
|
[SHVClient, CliConfig, CliItems, int | None], typing.Awaitable[None]
|
|
16
18
|
]
|
|
@@ -115,7 +117,10 @@ async def call_builtin(
|
|
|
115
117
|
return
|
|
116
118
|
|
|
117
119
|
if isinstance(m, Method):
|
|
118
|
-
|
|
120
|
+
res = m.func(shvclient, config, items)
|
|
121
|
+
if isinstance(res, collections.abc.Awaitable):
|
|
122
|
+
await res
|
|
123
|
+
return
|
|
119
124
|
|
|
120
125
|
typing.assert_type(m, XMethod)
|
|
121
126
|
strnum = items.method[len(m.name) + 1 :]
|
|
@@ -5,7 +5,6 @@ import inspect
|
|
|
5
5
|
import itertools
|
|
6
6
|
|
|
7
7
|
from prompt_toolkit.completion import Completion
|
|
8
|
-
from shv import RpcSubscription
|
|
9
8
|
|
|
10
9
|
from .builtin import METHODS, Argument, XMethod, builtin, xbuiltin
|
|
11
10
|
from .client import Node, SHVClient
|
|
@@ -14,7 +13,7 @@ from .config import CliConfig
|
|
|
14
13
|
from .lsdir import ls_node_format
|
|
15
14
|
from .parse import CliItems
|
|
16
15
|
from .scan import scan_nodes
|
|
17
|
-
from .tools import lookahead, print_block, print_ftext, print_row
|
|
16
|
+
from .tools import lookahead, print_block, print_flist, print_ftext, print_row
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
def argument_signal_comp(
|
|
@@ -22,11 +21,13 @@ def argument_signal_comp(
|
|
|
22
21
|
) -> collections.abc.Iterable[Completion]:
|
|
23
22
|
"""Completion for subscribe argument."""
|
|
24
23
|
if ":" in items.param_raw:
|
|
25
|
-
path, method = items.
|
|
26
|
-
assert method is not None
|
|
24
|
+
path, method, signal = items.interpret_param_ri(config)[-1].split(":")
|
|
27
25
|
node = shvclient.tree.get_path(path)
|
|
28
26
|
if node is not None:
|
|
29
|
-
|
|
27
|
+
if items.param_raw.rsplit(maxsplit=1)[-1].count(":") == 1:
|
|
28
|
+
yield from comp_from(method, node.methods)
|
|
29
|
+
elif method in node.methods:
|
|
30
|
+
yield from comp_from(signal, node.methods[method])
|
|
30
31
|
else:
|
|
31
32
|
yield from comp_path(shvclient, config, items)
|
|
32
33
|
|
|
@@ -53,7 +54,7 @@ argument_set = Argument("[OPTION [VALUE]]", argument_set_comp)
|
|
|
53
54
|
|
|
54
55
|
|
|
55
56
|
@builtin("help", {"h"}, hidden=True)
|
|
56
|
-
|
|
57
|
+
def _help(_: SHVClient, __: CliConfig, ___: CliItems) -> None:
|
|
57
58
|
"""Print help for all builtin methods."""
|
|
58
59
|
print_row("Available internal methods (all prefixed with '!'):")
|
|
59
60
|
for name, m in METHODS.items():
|
|
@@ -70,25 +71,32 @@ async def _help(_: SHVClient, __: CliConfig, ___: CliItems) -> None:
|
|
|
70
71
|
@builtin(aliases={"sub"}, argument=argument_signal)
|
|
71
72
|
async def subscribe(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
72
73
|
"""Add new subscribe."""
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
for ri in items.interpret_param_ri(config):
|
|
75
|
+
await shvclient.subscribe(ri)
|
|
75
76
|
|
|
76
77
|
|
|
77
78
|
@builtin(aliases={"usub"}, argument=argument_signal)
|
|
78
79
|
async def unsubscribe(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
79
80
|
"""Unsubscribe existing subscription."""
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
for ri in items.interpret_param_ri(config):
|
|
82
|
+
await shvclient.unsubscribe(ri)
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
@builtin(aliases={"subs", "test"})
|
|
85
86
|
async def subscriptions(shvclient: SHVClient, _: CliConfig, __: CliItems) -> None:
|
|
86
87
|
"""List current subscriptions."""
|
|
87
|
-
|
|
88
|
+
subs = await shvclient.call(".broker/currentClient", "subscriptions")
|
|
89
|
+
if isinstance(subs, dict):
|
|
90
|
+
print_flist(
|
|
91
|
+
("", sub + (f"?{timeout}secs" if timeout is not None else ""))
|
|
92
|
+
for sub, timeout in subs.items()
|
|
93
|
+
)
|
|
94
|
+
else:
|
|
95
|
+
print(subs)
|
|
88
96
|
|
|
89
97
|
|
|
90
98
|
@builtin(aliases={"t"}, argument=argument_path)
|
|
91
|
-
|
|
99
|
+
def tree(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
92
100
|
"""Print tree of nodes discovered in this session."""
|
|
93
101
|
|
|
94
102
|
def print_node(node: Node, cols: list[bool]) -> None:
|
|
@@ -126,7 +134,7 @@ async def scan(
|
|
|
126
134
|
|
|
127
135
|
|
|
128
136
|
@builtin("set", aliases={"s"}, argument=argument_set)
|
|
129
|
-
|
|
137
|
+
def _set(_: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
130
138
|
"""Set configuration options in runtime."""
|
|
131
139
|
opt, val = items.interpret_param_set()
|
|
132
140
|
if not opt:
|
|
@@ -52,17 +52,19 @@ async def _app(config: CliConfig, shvclient: SHVClient) -> None:
|
|
|
52
52
|
[prompt_path, ("", "> ")], vi_mode=config.vimode
|
|
53
53
|
)
|
|
54
54
|
except EOFError:
|
|
55
|
-
shvclient.
|
|
55
|
+
await shvclient.disconnect()
|
|
56
56
|
return
|
|
57
57
|
await handle_line(shvclient, config, result)
|
|
58
58
|
except KeyboardInterrupt:
|
|
59
59
|
continue
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
async def run(config: CliConfig) -> None:
|
|
62
|
+
async def run(config: CliConfig, subscriptions: list[str]) -> None:
|
|
63
63
|
"""Loop to run interactive CLI session."""
|
|
64
64
|
shvclient = await SHVClient.connect(config.url)
|
|
65
65
|
assert isinstance(shvclient, SHVClient)
|
|
66
|
+
for ri in subscriptions:
|
|
67
|
+
await shvclient.subscribe(ri)
|
|
66
68
|
|
|
67
69
|
if config.cache:
|
|
68
70
|
cacheurl = RpcUrl(
|
|
@@ -15,9 +15,9 @@ from shv import (
|
|
|
15
15
|
SHVType,
|
|
16
16
|
SimpleClient,
|
|
17
17
|
)
|
|
18
|
-
from shv.cpon import Cpon
|
|
19
18
|
|
|
20
19
|
from . import VERSION
|
|
20
|
+
from .tools import print_cpon
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class Node(collections.abc.Mapping[str, "Node"]):
|
|
@@ -26,8 +26,7 @@ class Node(collections.abc.Mapping[str, "Node"]):
|
|
|
26
26
|
def __init__(self) -> None:
|
|
27
27
|
"""Initialize the node."""
|
|
28
28
|
self.nodes: dict[str, Node] = {}
|
|
29
|
-
self.methods: set[str] = {"ls", "dir"}
|
|
30
|
-
self.signals: set[str] = set()
|
|
29
|
+
self.methods: dict[str, set[str]] = {"ls": {"lsmod"}, "dir": set()}
|
|
31
30
|
self.nodes_probed = False
|
|
32
31
|
self.methods_probed = False
|
|
33
32
|
|
|
@@ -83,8 +82,7 @@ class Node(collections.abc.Mapping[str, "Node"]):
|
|
|
83
82
|
"""Dump the data to basic types."""
|
|
84
83
|
return {
|
|
85
84
|
"nodes": {n: v.dump() for n, v in self.nodes.items()},
|
|
86
|
-
"methods": list(self.methods),
|
|
87
|
-
"signals": list(self.signals),
|
|
85
|
+
"methods": {n: list(v) for n, v in self.methods.items()},
|
|
88
86
|
"nodes_probed": self.nodes_probed,
|
|
89
87
|
"methods_probed": self.methods_probed,
|
|
90
88
|
}
|
|
@@ -96,10 +94,8 @@ class Node(collections.abc.Mapping[str, "Node"]):
|
|
|
96
94
|
res = cls()
|
|
97
95
|
if isinstance(data["nodes"], collections.abc.Mapping):
|
|
98
96
|
res.nodes = {n: cls.load(v) for n, v in data["nodes"].items()}
|
|
99
|
-
if isinstance(data["methods"], collections.abc.
|
|
100
|
-
res.methods = set(data["methods"])
|
|
101
|
-
if isinstance(data["signals"], collections.abc.Sequence):
|
|
102
|
-
res.signals = set(data["signals"])
|
|
97
|
+
if isinstance(data["methods"], collections.abc.Mapping):
|
|
98
|
+
res.methods = {str(n): set(v) for n, v in data["methods"].items()}
|
|
103
99
|
res.nodes_probed = bool(data["nodes_probed"])
|
|
104
100
|
res.methods_probed = bool(data["methods_probed"])
|
|
105
101
|
return res
|
|
@@ -124,10 +120,10 @@ class SHVClient(SimpleClient):
|
|
|
124
120
|
async def _message(self, msg: RpcMessage) -> None:
|
|
125
121
|
await super()._message(msg)
|
|
126
122
|
if msg.is_signal:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
self.tree.valid_path(msg.path or "").methods.setdefault(
|
|
124
|
+
msg.source, set()
|
|
125
|
+
).add(msg.signal_name)
|
|
126
|
+
print_cpon(msg.param, f"{msg.path}:{msg.source}:{msg.signal_name}: ", True)
|
|
131
127
|
|
|
132
128
|
async def ls(self, path: str) -> list[str]:
|
|
133
129
|
"""List same as in ValueClient but with result being preserved in tree."""
|
|
@@ -154,9 +150,10 @@ class SHVClient(SimpleClient):
|
|
|
154
150
|
raise exc
|
|
155
151
|
node = self.tree.valid_path(path)
|
|
156
152
|
node.methods = {
|
|
157
|
-
d.name
|
|
153
|
+
d.name: set(d.signals)
|
|
154
|
+
for d in res
|
|
155
|
+
if RpcMethodFlags.NOT_CALLABLE not in d.flags
|
|
158
156
|
}
|
|
159
|
-
node.signals = {d.name for d in res if RpcMethodFlags.NOT_CALLABLE in d.flags}
|
|
160
157
|
node.methods_probed = True
|
|
161
158
|
return res
|
|
162
159
|
|
|
@@ -177,9 +174,9 @@ class SHVClient(SimpleClient):
|
|
|
177
174
|
except RpcMethodNotFoundError as exc:
|
|
178
175
|
raise exc
|
|
179
176
|
except RpcError as exc:
|
|
180
|
-
self.tree.valid_path(path).methods.
|
|
177
|
+
self.tree.valid_path(path).methods.setdefault(method, set())
|
|
181
178
|
raise exc
|
|
182
|
-
self.tree.valid_path(path).methods.
|
|
179
|
+
self.tree.valid_path(path).methods.setdefault(method, set())
|
|
183
180
|
return res
|
|
184
181
|
|
|
185
182
|
async def probe(self, path: str) -> Node | None:
|
|
@@ -61,7 +61,8 @@ class CliCompleter(Completer):
|
|
|
61
61
|
CliFlags.COMPLETE_CALL not in items.flags
|
|
62
62
|
or items.method in {"ls", "dir"}
|
|
63
63
|
or (
|
|
64
|
-
items.method
|
|
64
|
+
items.method
|
|
65
|
+
and items.method[0] == "!"
|
|
65
66
|
and (bmethod := builtin.get_builtin(items.method[1:])) is not None
|
|
66
67
|
and bmethod.argument
|
|
67
68
|
and bmethod.argument.autoprobe
|
|
@@ -101,9 +101,9 @@ class CliConfig:
|
|
|
101
101
|
self.__rurl = self.__url
|
|
102
102
|
elif isinstance(self.__url, str):
|
|
103
103
|
self.__rurl = RpcUrl.parse(
|
|
104
|
-
subprocess.run(
|
|
104
|
+
subprocess.run( # noqa S602
|
|
105
105
|
f"printf '%s' \"{self.__url}\"",
|
|
106
|
-
shell=True,
|
|
106
|
+
shell=True,
|
|
107
107
|
stdout=subprocess.PIPE,
|
|
108
108
|
check=True,
|
|
109
109
|
).stdout.decode()
|
|
@@ -42,7 +42,7 @@ def dir_method_format(method: RpcMethodDesc) -> tuple[str, str]:
|
|
|
42
42
|
methstyle = "ansimagenta"
|
|
43
43
|
else:
|
|
44
44
|
methstyle = "ansigreen"
|
|
45
|
-
elif method.signals
|
|
45
|
+
elif method.signals:
|
|
46
46
|
methstyle = "ansipurple"
|
|
47
47
|
name = method.name
|
|
48
48
|
return (
|
|
@@ -51,6 +51,20 @@ def dir_method_format(method: RpcMethodDesc) -> tuple[str, str]:
|
|
|
51
51
|
)
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def dir_signal_format(method: RpcMethodDesc, signal: str) -> tuple[str, str]:
|
|
55
|
+
"""Print format for signals."""
|
|
56
|
+
sigstyle = "ansipurple"
|
|
57
|
+
if method.name in {"ls", "dir"}:
|
|
58
|
+
sigstyle = "ansibrightblack"
|
|
59
|
+
elif RpcMethodFlags.GETTER in method.flags:
|
|
60
|
+
sigstyle = "ansimagenta"
|
|
61
|
+
label = f"{method.name}:{signal}"
|
|
62
|
+
return (
|
|
63
|
+
sigstyle,
|
|
64
|
+
f'"{label}"' if any(c in label for c in string.whitespace) else label,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
54
68
|
async def ls_method(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
55
69
|
"""SHV ls method that is just smarter than regular call."""
|
|
56
70
|
shvpath = items.interpret_param_path(config)
|
|
@@ -94,6 +108,8 @@ async def dir_method(shvclient: SHVClient, config: CliConfig, items: CliItems) -
|
|
|
94
108
|
if config.autoget and any(_use_autoget(d) for d in dirr):
|
|
95
109
|
w = max(len(d.name) for d in dirr)
|
|
96
110
|
for d in dirr:
|
|
111
|
+
if RpcMethodFlags.NOT_CALLABLE in d.flags:
|
|
112
|
+
continue # Ignore not callable
|
|
97
113
|
n = [("", " " * (w - len(d.name))), dir_method_format(d)]
|
|
98
114
|
if _use_autoget(d):
|
|
99
115
|
try:
|
|
@@ -110,6 +126,8 @@ async def dir_method(shvclient: SHVClient, config: CliConfig, items: CliItems) -
|
|
|
110
126
|
print_row(n)
|
|
111
127
|
else:
|
|
112
128
|
print_flist(dir_method_format(d) for d in dirr)
|
|
129
|
+
if any(d.signals for d in dirr):
|
|
130
|
+
print_flist(dir_signal_format(d, s) for d in dirr for s in d.signals)
|
|
113
131
|
|
|
114
132
|
|
|
115
133
|
def _use_autoget(method: RpcMethodDesc) -> bool:
|
|
@@ -39,16 +39,6 @@ class CliItems:
|
|
|
39
39
|
return None
|
|
40
40
|
return Cpon.unpack(self.param_raw)
|
|
41
41
|
|
|
42
|
-
@property
|
|
43
|
-
def param_method(self) -> tuple[str, str | None]:
|
|
44
|
-
"""Interpret parameter as path and method."""
|
|
45
|
-
if ":" in self.param_raw:
|
|
46
|
-
path, method = self.param_raw.split(":", maxsplit=1)
|
|
47
|
-
return path, method
|
|
48
|
-
if "/" in self.param_raw:
|
|
49
|
-
return self.param_raw, None
|
|
50
|
-
return "", self.param_raw
|
|
51
|
-
|
|
52
42
|
def interpret_param_path(self, config: CliConfig) -> str:
|
|
53
43
|
"""Interpret parameter as path specification.
|
|
54
44
|
|
|
@@ -56,16 +46,21 @@ class CliItems:
|
|
|
56
46
|
"""
|
|
57
47
|
return config.shvpath([self.path, self.param_raw])
|
|
58
48
|
|
|
59
|
-
def
|
|
60
|
-
"""Interpret parameter as
|
|
49
|
+
def interpret_param_ri(self, config: CliConfig, expand: bool = True) -> list[str]:
|
|
50
|
+
"""Interpret parameter as RI.
|
|
61
51
|
|
|
62
|
-
|
|
63
|
-
path and method not just parsed :prop:`param_raw`.
|
|
52
|
+
The path in RI is combined with pat in the configuration.
|
|
64
53
|
|
|
65
|
-
:return:
|
|
54
|
+
:return: SHV RPC RI.
|
|
66
55
|
"""
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
res = []
|
|
57
|
+
for item in self.param_raw.split():
|
|
58
|
+
parts = item.split(":")
|
|
59
|
+
path = parts[0] if parts[0] else "**"
|
|
60
|
+
method = parts[1] if len(parts) == 2 else "*"
|
|
61
|
+
signal = parts[2] if len(parts) == 3 else "*"
|
|
62
|
+
res.append(f"{self.path}{"/" if self.path else ""}{path}:{method}:{signal}")
|
|
63
|
+
return res
|
|
69
64
|
|
|
70
65
|
def interpret_param_set(self) -> tuple[str | None, str | None]:
|
|
71
66
|
"""Interpret parameter as 'set' method specification.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Various tools used in the code."""
|
|
2
2
|
|
|
3
3
|
import collections.abc
|
|
4
|
+
import itertools
|
|
4
5
|
import os
|
|
5
6
|
import textwrap
|
|
6
7
|
import typing
|
|
@@ -118,16 +119,31 @@ def cpon_ftext(cpon: str) -> collections.abc.Iterator[tuple[str, str]]:
|
|
|
118
119
|
yield "", "\n"
|
|
119
120
|
|
|
120
121
|
|
|
121
|
-
def
|
|
122
|
+
def _wrap_cpon(cpon: str) -> collections.abc.Iterator[str]:
|
|
123
|
+
"""Wrap CPON.
|
|
124
|
+
|
|
125
|
+
It is better to wrap CPON on division characters rather than white spaces
|
|
126
|
+
because those are part of the data.
|
|
127
|
+
"""
|
|
128
|
+
cols = os.get_terminal_size().columns
|
|
129
|
+
while len(cpon) > cols:
|
|
130
|
+
i = max(cpon.rfind(sep, 0, cols) for sep in (",", "]", "}"))
|
|
131
|
+
if i == -1:
|
|
132
|
+
i = cols
|
|
133
|
+
yield cpon[: i + 1]
|
|
134
|
+
cpon = " " + cpon[i + 1 :]
|
|
135
|
+
yield cpon
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def print_cpon(data: shv.SHVType, prefix: str = "", short: bool = False) -> None:
|
|
122
139
|
"""Print given data in CPON format."""
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
print_row(cpon_ftext(simple))
|
|
127
|
-
else:
|
|
128
|
-
for line in shv.Cpon.pack(
|
|
129
|
-
data, shv.CponWriter.Options(indent=b" ")
|
|
130
|
-
).splitlines():
|
|
131
|
-
print_row(cpon_ftext(line))
|
|
140
|
+
if short:
|
|
141
|
+
cpon = "\n".join(_wrap_cpon(prefix + shv.Cpon.pack(data)))[len(prefix) :]
|
|
142
|
+
print_ftext(itertools.chain(iter((("", prefix),)), cpon_ftext(cpon)))
|
|
132
143
|
else:
|
|
133
|
-
|
|
144
|
+
print_row(
|
|
145
|
+
itertools.chain(
|
|
146
|
+
iter((("", prefix),)),
|
|
147
|
+
cpon_ftext(shv.Cpon.pack(data, shv.CponWriter.Options(indent=b" "))),
|
|
148
|
+
)
|
|
149
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: shvcli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
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.7.1
|
|
42
42
|
Requires-Dist: prompt_toolkit
|
|
43
43
|
Requires-Dist: pygments
|
|
44
44
|
Requires-Dist: pyxdg
|
|
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
|