shvcli 0.5.0__tar.gz → 0.6.1__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.1}/PKG-INFO +8 -2
- {shvcli-0.5.0 → shvcli-0.6.1}/README.rst +6 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/pyproject.toml +2 -2
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/__main__.py +7 -1
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/builtin_impl.py +15 -6
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/cli.py +10 -2
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/client.py +2 -2
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/config.py +25 -5
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/lsdir.py +31 -5
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/parse.py +9 -6
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/tools.py +27 -11
- {shvcli-0.5.0 → shvcli-0.6.1/shvcli.egg-info}/PKG-INFO +8 -2
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli.egg-info/requires.txt +1 -1
- {shvcli-0.5.0 → shvcli-0.6.1}/LICENSE +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/setup.cfg +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/__init__.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/__version__.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/builtin.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/complet.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/complet_tools.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/py.typed +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/scan.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/valid.py +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli/version +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli.egg-info/SOURCES.txt +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli.egg-info/dependency_links.txt +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/shvcli.egg-info/entry_points.txt +0 -0
- {shvcli-0.5.0 → shvcli-0.6.1}/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.1
|
|
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
|
|
@@ -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:
|
|
@@ -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:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "shvcli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.1"
|
|
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(
|
|
@@ -115,9 +117,15 @@ async def handle_line(shvclient: SHVClient, config: CliConfig, cmdline: str) ->
|
|
|
115
117
|
else:
|
|
116
118
|
print_cpon(
|
|
117
119
|
await shvclient.call(
|
|
118
|
-
config.shvpath(items.path),
|
|
120
|
+
config.shvpath(items.path),
|
|
121
|
+
items.method,
|
|
122
|
+
param,
|
|
123
|
+
call_attempts=config.call_attempts,
|
|
124
|
+
call_timeout=config.call_timeout,
|
|
119
125
|
)
|
|
120
126
|
)
|
|
127
|
+
except TimeoutError:
|
|
128
|
+
print("Call timed out.")
|
|
121
129
|
except RpcError as exc:
|
|
122
130
|
print(f"{type(exc).__name__}: {exc.message}")
|
|
123
131
|
else:
|
|
@@ -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."""
|
|
@@ -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
|
)
|
|
@@ -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)
|
|
@@ -71,9 +85,12 @@ async def ls_method(shvclient: SHVClient, config: CliConfig, items: CliItems) ->
|
|
|
71
85
|
if "get" in nv.methods:
|
|
72
86
|
try:
|
|
73
87
|
resp = await shvclient.call(
|
|
74
|
-
str(pathlib.PurePosixPath(shvpath) / nn),
|
|
88
|
+
str(pathlib.PurePosixPath(shvpath) / nn),
|
|
89
|
+
"get",
|
|
90
|
+
call_attempts=1,
|
|
91
|
+
call_timeout=config.autoget_timeout,
|
|
75
92
|
)
|
|
76
|
-
except RpcError:
|
|
93
|
+
except (RpcError, TimeoutError):
|
|
77
94
|
pass
|
|
78
95
|
else:
|
|
79
96
|
print_row(
|
|
@@ -94,11 +111,18 @@ async def dir_method(shvclient: SHVClient, config: CliConfig, items: CliItems) -
|
|
|
94
111
|
if config.autoget and any(_use_autoget(d) for d in dirr):
|
|
95
112
|
w = max(len(d.name) for d in dirr)
|
|
96
113
|
for d in dirr:
|
|
114
|
+
if RpcMethodFlags.NOT_CALLABLE in d.flags:
|
|
115
|
+
continue # Ignore not callable
|
|
97
116
|
n = [("", " " * (w - len(d.name))), dir_method_format(d)]
|
|
98
117
|
if _use_autoget(d):
|
|
99
118
|
try:
|
|
100
|
-
resp = await shvclient.call(
|
|
101
|
-
|
|
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):
|
|
102
126
|
pass
|
|
103
127
|
else:
|
|
104
128
|
print_row(
|
|
@@ -110,6 +134,8 @@ async def dir_method(shvclient: SHVClient, config: CliConfig, items: CliItems) -
|
|
|
110
134
|
print_row(n)
|
|
111
135
|
else:
|
|
112
136
|
print_flist(dir_method_format(d) for d in dirr)
|
|
137
|
+
if any(d.signals for d in dirr):
|
|
138
|
+
print_flist(dir_signal_format(d, s) for d in dirr for s in d.signals)
|
|
113
139
|
|
|
114
140
|
|
|
115
141
|
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
|
-
print_ftext(
|
|
144
|
+
print_ftext(
|
|
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.1
|
|
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
|
|
@@ -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:
|
|
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
|