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.
Files changed (28) hide show
  1. {shvcli-0.4.0/shvcli.egg-info → shvcli-0.6.0}/PKG-INFO +2 -2
  2. {shvcli-0.4.0 → shvcli-0.6.0}/pyproject.toml +3 -3
  3. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/__main__.py +7 -1
  4. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/__version__.py +5 -4
  5. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/builtin.py +7 -2
  6. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/builtin_impl.py +21 -13
  7. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/cli.py +4 -2
  8. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/client.py +14 -17
  9. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/complet.py +2 -1
  10. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/config.py +2 -2
  11. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/lsdir.py +19 -1
  12. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/parse.py +12 -17
  13. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/tools.py +27 -11
  14. {shvcli-0.4.0 → shvcli-0.6.0/shvcli.egg-info}/PKG-INFO +2 -2
  15. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/requires.txt +1 -1
  16. {shvcli-0.4.0 → shvcli-0.6.0}/LICENSE +0 -0
  17. {shvcli-0.4.0 → shvcli-0.6.0}/README.rst +0 -0
  18. {shvcli-0.4.0 → shvcli-0.6.0}/setup.cfg +0 -0
  19. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/__init__.py +0 -0
  20. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/complet_tools.py +0 -0
  21. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/py.typed +0 -0
  22. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/scan.py +0 -0
  23. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/valid.py +0 -0
  24. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli/version +0 -0
  25. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/SOURCES.txt +0 -0
  26. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/dependency_links.txt +0 -0
  27. {shvcli-0.4.0 → shvcli-0.6.0}/shvcli.egg-info/entry_points.txt +0 -0
  28. {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.4.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.6.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.4.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.6.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", "TRIO", "S", "BLE",
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
- with pyproject.open("rb") as f:
19
- res = tomllib.load(f)["project"]["version"]
20
- if isinstance(res, str):
21
- return res
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[[SHVClient, CliConfig, CliItems], typing.Awaitable[None]]
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
- return await m.func(shvclient, config, items)
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.interpret_param_method(config)
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
- yield from comp_from(method, node.signals)
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
- async def _help(_: SHVClient, __: CliConfig, ___: CliItems) -> None:
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
- path, method = items.interpret_param_method(config)
74
- await shvclient.subscribe(RpcSubscription(path, method or ""))
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
- path, method = items.interpret_param_method(config)
81
- await shvclient.unsubscribe(RpcSubscription(path, method or ""))
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
- print(await shvclient.call(".app/broker/currentClient", "subscriptions"))
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
- async def tree(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
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
- async def _set(_: SHVClient, config: CliConfig, items: CliItems) -> None:
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.client.disconnect()
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.Sequence):
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
- method = msg.method
128
- assert method is not None
129
- self.tree.valid_path(msg.path or "").signals.add(method)
130
- print(f"{msg.path}:{method}: {Cpon.pack(msg.param)}")
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 for d in res if RpcMethodFlags.NOT_CALLABLE not in d.flags
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.add(method)
177
+ self.tree.valid_path(path).methods.setdefault(method, set())
181
178
  raise exc
182
- self.tree.valid_path(path).methods.add(method)
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[0] == "!"
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, # noqa S602
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 or RpcMethodFlags.NOT_CALLABLE in method.flags:
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 interpret_param_method(self, config: CliConfig) -> tuple[str, str | None]:
60
- """Interpret parameter as method specification.
49
+ def interpret_param_ri(self, config: CliConfig, expand: bool = True) -> list[str]:
50
+ """Interpret parameter as RI.
61
51
 
62
- Compared to :meth:`param_method` this provides a full combination of
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: pair of SHV path and method name that is from parameter.
54
+ :return: SHV RPC RI.
66
55
  """
67
- param_path, method = self.param_method
68
- return config.shvpath([self.path, param_path]), method
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 print_cpon(data: shv.SHVType, short: bool = False) -> None:
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
- simple = shv.Cpon.pack(data)
124
- if len(simple) > os.get_terminal_size().columns:
125
- if short:
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(cpon_ftext(simple))
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.4.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.6.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,4 +1,4 @@
1
- pyshv>=0.6.0
1
+ pyshv>=0.7.1
2
2
  prompt_toolkit
3
3
  pygments
4
4
  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