shvcli 0.5.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.5.0/shvcli.egg-info → shvcli-0.6.0}/PKG-INFO +2 -2
- {shvcli-0.5.0 → shvcli-0.6.0}/pyproject.toml +2 -2
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/__main__.py +7 -1
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/builtin_impl.py +15 -6
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/cli.py +3 -1
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/client.py +2 -2
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/lsdir.py +19 -1
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/parse.py +9 -6
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/tools.py +27 -11
- {shvcli-0.5.0 → shvcli-0.6.0/shvcli.egg-info}/PKG-INFO +2 -2
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli.egg-info/requires.txt +1 -1
- {shvcli-0.5.0 → shvcli-0.6.0}/LICENSE +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/README.rst +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/setup.cfg +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/__init__.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/__version__.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/builtin.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/complet.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/complet_tools.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/config.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/py.typed +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/scan.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/valid.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli/version +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli.egg-info/SOURCES.txt +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli.egg-info/dependency_links.txt +0 -0
- {shvcli-0.5.0 → shvcli-0.6.0}/shvcli.egg-info/entry_points.txt +0 -0
- {shvcli-0.5.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.7.
|
|
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.7.
|
|
23
|
+
"pyshv >= 0.7.1",
|
|
24
24
|
"prompt_toolkit",
|
|
25
25
|
"pygments",
|
|
26
26
|
"pyxdg",
|
|
@@ -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__":
|
|
@@ -13,7 +13,7 @@ from .config import CliConfig
|
|
|
13
13
|
from .lsdir import ls_node_format
|
|
14
14
|
from .parse import CliItems
|
|
15
15
|
from .scan import scan_nodes
|
|
16
|
-
from .tools import lookahead, print_block, print_ftext, print_row
|
|
16
|
+
from .tools import lookahead, print_block, print_flist, print_ftext, print_row
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def argument_signal_comp(
|
|
@@ -21,10 +21,10 @@ def argument_signal_comp(
|
|
|
21
21
|
) -> collections.abc.Iterable[Completion]:
|
|
22
22
|
"""Completion for subscribe argument."""
|
|
23
23
|
if ":" in items.param_raw:
|
|
24
|
-
path, method, signal = items.interpret_param_ri(config).split(":")
|
|
24
|
+
path, method, signal = items.interpret_param_ri(config)[-1].split(":")
|
|
25
25
|
node = shvclient.tree.get_path(path)
|
|
26
26
|
if node is not None:
|
|
27
|
-
if items.param_raw.count(":") == 1:
|
|
27
|
+
if items.param_raw.rsplit(maxsplit=1)[-1].count(":") == 1:
|
|
28
28
|
yield from comp_from(method, node.methods)
|
|
29
29
|
elif method in node.methods:
|
|
30
30
|
yield from comp_from(signal, node.methods[method])
|
|
@@ -71,19 +71,28 @@ def _help(_: SHVClient, __: CliConfig, ___: CliItems) -> None:
|
|
|
71
71
|
@builtin(aliases={"sub"}, argument=argument_signal)
|
|
72
72
|
async def subscribe(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
73
73
|
"""Add new subscribe."""
|
|
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
|
+
for ri in items.interpret_param_ri(config):
|
|
82
|
+
await shvclient.unsubscribe(ri)
|
|
81
83
|
|
|
82
84
|
|
|
83
85
|
@builtin(aliases={"subs", "test"})
|
|
84
86
|
async def subscriptions(shvclient: SHVClient, _: CliConfig, __: CliItems) -> None:
|
|
85
87
|
"""List current subscriptions."""
|
|
86
|
-
|
|
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)
|
|
87
96
|
|
|
88
97
|
|
|
89
98
|
@builtin(aliases={"t"}, argument=argument_path)
|
|
@@ -59,10 +59,12 @@ async def _app(config: CliConfig, shvclient: SHVClient) -> None:
|
|
|
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"]):
|
|
@@ -123,7 +123,7 @@ class SHVClient(SimpleClient):
|
|
|
123
123
|
self.tree.valid_path(msg.path or "").methods.setdefault(
|
|
124
124
|
msg.source, set()
|
|
125
125
|
).add(msg.signal_name)
|
|
126
|
-
|
|
126
|
+
print_cpon(msg.param, f"{msg.path}:{msg.source}:{msg.signal_name}: ", True)
|
|
127
127
|
|
|
128
128
|
async def ls(self, path: str) -> list[str]:
|
|
129
129
|
"""List same as in ValueClient but with result being preserved in tree."""
|
|
@@ -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:
|
|
@@ -46,18 +46,21 @@ class CliItems:
|
|
|
46
46
|
"""
|
|
47
47
|
return config.shvpath([self.path, self.param_raw])
|
|
48
48
|
|
|
49
|
-
def interpret_param_ri(self, config: CliConfig, expand: bool = True) -> str:
|
|
49
|
+
def interpret_param_ri(self, config: CliConfig, expand: bool = True) -> list[str]:
|
|
50
50
|
"""Interpret parameter as RI.
|
|
51
51
|
|
|
52
52
|
The path in RI is combined with pat in the configuration.
|
|
53
53
|
|
|
54
54
|
:return: SHV RPC RI.
|
|
55
55
|
"""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
61
64
|
|
|
62
65
|
def interpret_param_set(self) -> tuple[str | None, str | None]:
|
|
63
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.7.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|