shvcli 0.6.2__tar.gz → 0.7.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 (57) hide show
  1. {shvcli-0.6.2/shvcli.egg-info → shvcli-0.7.0}/PKG-INFO +37 -17
  2. {shvcli-0.6.2 → shvcli-0.7.0}/README.rst +32 -13
  3. {shvcli-0.6.2 → shvcli-0.7.0}/pyproject.toml +3 -3
  4. shvcli-0.7.0/shvcli/__init__.py +55 -0
  5. shvcli-0.7.0/shvcli/__main__.py +36 -0
  6. shvcli-0.7.0/shvcli/afterconn.py +55 -0
  7. shvcli-0.7.0/shvcli/args.py +56 -0
  8. shvcli-0.7.0/shvcli/builtin.py +86 -0
  9. shvcli-0.7.0/shvcli/builtins/__init__.py +27 -0
  10. shvcli-0.7.0/shvcli/builtins/broker.py +100 -0
  11. shvcli-0.7.0/shvcli/builtins/cd.py +37 -0
  12. shvcli-0.7.0/shvcli/builtins/file.py +128 -0
  13. shvcli-0.7.0/shvcli/builtins/help.py +58 -0
  14. shvcli-0.7.0/shvcli/builtins/scan.py +41 -0
  15. shvcli-0.7.0/shvcli/builtins/set.py +70 -0
  16. shvcli-0.7.0/shvcli/builtins/tree.py +66 -0
  17. shvcli-0.7.0/shvcli/cli.py +184 -0
  18. shvcli-0.7.0/shvcli/client.py +133 -0
  19. shvcli-0.7.0/shvcli/cliitems.py +88 -0
  20. shvcli-0.7.0/shvcli/complet.py +86 -0
  21. shvcli-0.7.0/shvcli/config.py +106 -0
  22. shvcli-0.7.0/shvcli/file.py +68 -0
  23. shvcli-0.7.0/shvcli/lsdir.py +127 -0
  24. shvcli-0.7.0/shvcli/option.py +267 -0
  25. shvcli-0.7.0/shvcli/options.py +168 -0
  26. {shvcli-0.6.2 → shvcli-0.7.0}/shvcli/scan.py +7 -7
  27. shvcli-0.7.0/shvcli/state.py +96 -0
  28. shvcli-0.7.0/shvcli/tools/__init__.py +1 -0
  29. shvcli-0.7.0/shvcli/tools/complet.py +60 -0
  30. shvcli-0.6.2/shvcli/tools.py → shvcli-0.7.0/shvcli/tools/print.py +15 -28
  31. shvcli-0.7.0/shvcli/tree.py +126 -0
  32. shvcli-0.7.0/shvcli/url.py +110 -0
  33. shvcli-0.7.0/shvcli/valid.py +78 -0
  34. {shvcli-0.6.2 → shvcli-0.7.0/shvcli.egg-info}/PKG-INFO +37 -17
  35. shvcli-0.7.0/shvcli.egg-info/SOURCES.txt +42 -0
  36. {shvcli-0.6.2 → shvcli-0.7.0}/shvcli.egg-info/requires.txt +2 -1
  37. shvcli-0.6.2/shvcli/__init__.py +0 -5
  38. shvcli-0.6.2/shvcli/__main__.py +0 -62
  39. shvcli-0.6.2/shvcli/builtin.py +0 -132
  40. shvcli-0.6.2/shvcli/builtin_impl.py +0 -193
  41. shvcli-0.6.2/shvcli/cli.py +0 -138
  42. shvcli-0.6.2/shvcli/client.py +0 -210
  43. shvcli-0.6.2/shvcli/complet.py +0 -78
  44. shvcli-0.6.2/shvcli/complet_tools.py +0 -64
  45. shvcli-0.6.2/shvcli/config.py +0 -176
  46. shvcli-0.6.2/shvcli/lsdir.py +0 -145
  47. shvcli-0.6.2/shvcli/parse.py +0 -79
  48. shvcli-0.6.2/shvcli/valid.py +0 -41
  49. shvcli-0.6.2/shvcli.egg-info/SOURCES.txt +0 -26
  50. {shvcli-0.6.2 → shvcli-0.7.0}/LICENSE +0 -0
  51. {shvcli-0.6.2 → shvcli-0.7.0}/setup.cfg +0 -0
  52. {shvcli-0.6.2 → shvcli-0.7.0}/shvcli/__version__.py +0 -0
  53. {shvcli-0.6.2 → shvcli-0.7.0}/shvcli/plugins.py +0 -0
  54. {shvcli-0.6.2 → shvcli-0.7.0}/shvcli/py.typed +0 -0
  55. {shvcli-0.6.2 → shvcli-0.7.0}/shvcli.egg-info/dependency_links.txt +0 -0
  56. {shvcli-0.6.2 → shvcli-0.7.0}/shvcli.egg-info/entry_points.txt +0 -0
  57. {shvcli-0.6.2 → shvcli-0.7.0}/shvcli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: shvcli
3
- Version: 0.6.2
3
+ Version: 0.7.0
4
4
  Summary: Silicon Heaven CLI access application
5
5
  Author: Elektroline a.s.
6
6
  License: MIT License
@@ -27,7 +27,6 @@ License: MIT License
27
27
 
28
28
  Project-URL: repository, https://gitlab.com/silicon-heaven/shvcli
29
29
  Classifier: Development Status :: 4 - Beta
30
- Classifier: License :: OSI Approved :: MIT License
31
30
  Classifier: Programming Language :: Python :: 3
32
31
  Classifier: Operating System :: OS Independent
33
32
  Classifier: Environment :: Console
@@ -38,10 +37,12 @@ Classifier: Topic :: Terminals
38
37
  Requires-Python: >=3.11
39
38
  Description-Content-Type: text/x-rst
40
39
  License-File: LICENSE
41
- Requires-Dist: pyshv>=0.8.0
40
+ Requires-Dist: pyshv>=0.9.0
42
41
  Requires-Dist: prompt_toolkit
43
42
  Requires-Dist: pygments
44
43
  Requires-Dist: pyxdg
44
+ Requires-Dist: more-itertools
45
+ Dynamic: license-file
45
46
 
46
47
  =====================================
47
48
  Silicon Heaven CLI access application
@@ -101,20 +102,25 @@ An example of usage:
101
102
  .app> /:
102
103
  >
103
104
 
105
+ .. TIP::
106
+ The validation that is performed by SHVCLI is best effort and sometimes you
107
+ might need to submit call that is considered invalid by SHVCLI. For that
108
+ reason you can use `Ctrl+o` shortcut instead of pressing enter key.
109
+
104
110
 
105
111
  Configuration file
106
112
  ------------------
107
113
 
108
- Tool reads configuration from files ``/etc/shvcli.ini`` and ``~/.shvcli.ini``.
109
- They are in INI file format and the following sections are supported:
114
+ Tool reads configuration from files ``/etc/shvcli.toml`` and ``~/.shvcli.toml``.
115
+ They are in TOML file format and the following sections are supported:
110
116
 
111
117
  **hosts**: That provides mapping from some name to RPC URL.
112
118
 
113
119
  **hosts-shell**: That is same as **hosts** with exception that URL is passed
114
120
  through your local Shell to expand any variables or command substitutions.
115
121
 
116
- **config**: That allows you to set some initial configuration that can be also
117
- switched in runtime. The following options are available:
122
+ **option**: That allows you to set initial setting for runtime options. The
123
+ following options are available without any plugins:
118
124
 
119
125
  * **vimode**: If Vi input mode should be used for command line input. The
120
126
  default is ``false``.
@@ -137,17 +143,15 @@ switched in runtime. The following options are available:
137
143
  be also overwhelming. The default is ``false``. It is beneficial to disable
138
144
  the **autobrobe** once you enable debug because otherwise output on CLI will
139
145
  be mangled on completion.
140
- * **cache**: Controls if cache is preserved between invocations. Cache is
141
- separated based on the connection URL and stored in files in
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
146
+ * **call_query_timeout**: Timeout in seconds for status request query. The
147
+ shorter time will result in faster request lost detection while very short
148
+ time will load SHV network too much.
149
+ * **call_retry_timeout**: Timeout in seconds when call request is sent again if
150
+ no response is received. This is not applied if query is and thus this mostly
151
+ is used only in case when method doesn't support delayed responses.
152
+ * **autoget_timeout**: Timeout in seconds for call that is part of autoget
148
153
  functionality.
149
154
 
150
-
151
155
  Example configuration file:
152
156
 
153
157
  .. code-block:: ini
@@ -200,6 +204,15 @@ disable ``raw`` you use ``noraw``). You can also add ``=true`` or ``=false``.
200
204
  The equal sign is also used with integer and floating point options. Without any
201
205
  configuration option it simply prints the current configuration.
202
206
 
207
+ **upload**: provides a way to copy local file to the RPC File node. The
208
+ parameter must be path to the local file to be uploaded.
209
+
210
+ **download**: provides a way to copy RPC File node data to local file. The
211
+ parameter must be path to the local file where data will be stored.
212
+
213
+ **verify**: provides a way to verify RPC File node against local file. The
214
+ parameter must be path to the local file used for the verification.
215
+
203
216
 
204
217
  Special methods ``ls`` and ``dir``
205
218
  ----------------------------------
@@ -211,3 +224,10 @@ the fullest content.
211
224
  Their parameter is also handled in a special way. It is considered to be
212
225
  additional path suffix unless it is a valid CPON. This is allowed to match the
213
226
  common shells.
227
+
228
+
229
+ Plugins
230
+ -------
231
+
232
+ The support for external plugins is provided. These plugins are discovered using
233
+ Python package metadata using the entry point ``shvcli.plugins``.
@@ -56,20 +56,25 @@ An example of usage:
56
56
  .app> /:
57
57
  >
58
58
 
59
+ .. TIP::
60
+ The validation that is performed by SHVCLI is best effort and sometimes you
61
+ might need to submit call that is considered invalid by SHVCLI. For that
62
+ reason you can use `Ctrl+o` shortcut instead of pressing enter key.
63
+
59
64
 
60
65
  Configuration file
61
66
  ------------------
62
67
 
63
- Tool reads configuration from files ``/etc/shvcli.ini`` and ``~/.shvcli.ini``.
64
- They are in INI file format and the following sections are supported:
68
+ Tool reads configuration from files ``/etc/shvcli.toml`` and ``~/.shvcli.toml``.
69
+ They are in TOML file format and the following sections are supported:
65
70
 
66
71
  **hosts**: That provides mapping from some name to RPC URL.
67
72
 
68
73
  **hosts-shell**: That is same as **hosts** with exception that URL is passed
69
74
  through your local Shell to expand any variables or command substitutions.
70
75
 
71
- **config**: That allows you to set some initial configuration that can be also
72
- switched in runtime. The following options are available:
76
+ **option**: That allows you to set initial setting for runtime options. The
77
+ following options are available without any plugins:
73
78
 
74
79
  * **vimode**: If Vi input mode should be used for command line input. The
75
80
  default is ``false``.
@@ -92,17 +97,15 @@ switched in runtime. The following options are available:
92
97
  be also overwhelming. The default is ``false``. It is beneficial to disable
93
98
  the **autobrobe** once you enable debug because otherwise output on CLI will
94
99
  be mangled on completion.
95
- * **cache**: Controls if cache is preserved between invocations. Cache is
96
- separated based on the connection URL and stored in files in
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
100
+ * **call_query_timeout**: Timeout in seconds for status request query. The
101
+ shorter time will result in faster request lost detection while very short
102
+ time will load SHV network too much.
103
+ * **call_retry_timeout**: Timeout in seconds when call request is sent again if
104
+ no response is received. This is not applied if query is and thus this mostly
105
+ is used only in case when method doesn't support delayed responses.
106
+ * **autoget_timeout**: Timeout in seconds for call that is part of autoget
103
107
  functionality.
104
108
 
105
-
106
109
  Example configuration file:
107
110
 
108
111
  .. code-block:: ini
@@ -155,6 +158,15 @@ disable ``raw`` you use ``noraw``). You can also add ``=true`` or ``=false``.
155
158
  The equal sign is also used with integer and floating point options. Without any
156
159
  configuration option it simply prints the current configuration.
157
160
 
161
+ **upload**: provides a way to copy local file to the RPC File node. The
162
+ parameter must be path to the local file to be uploaded.
163
+
164
+ **download**: provides a way to copy RPC File node data to local file. The
165
+ parameter must be path to the local file where data will be stored.
166
+
167
+ **verify**: provides a way to verify RPC File node against local file. The
168
+ parameter must be path to the local file used for the verification.
169
+
158
170
 
159
171
  Special methods ``ls`` and ``dir``
160
172
  ----------------------------------
@@ -166,3 +178,10 @@ the fullest content.
166
178
  Their parameter is also handled in a special way. It is considered to be
167
179
  additional path suffix unless it is a valid CPON. This is allowed to match the
168
180
  common shells.
181
+
182
+
183
+ Plugins
184
+ -------
185
+
186
+ The support for external plugins is provided. These plugins are discovered using
187
+ Python package metadata using the entry point ``shvcli.plugins``.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "shvcli"
3
- version = "0.6.2"
3
+ version = "0.7.0"
4
4
  description = "Silicon Heaven CLI access application"
5
5
  readme = "README.rst"
6
6
  license = {file = "LICENSE"}
@@ -9,7 +9,6 @@ authors = [
9
9
  ]
10
10
  classifiers = [
11
11
  "Development Status :: 4 - Beta",
12
- "License :: OSI Approved :: MIT License",
13
12
  "Programming Language :: Python :: 3",
14
13
  "Operating System :: OS Independent",
15
14
  "Environment :: Console",
@@ -20,10 +19,11 @@ classifiers = [
20
19
  ]
21
20
  requires-python = ">=3.11"
22
21
  dependencies = [
23
- "pyshv >= 0.8.0",
22
+ "pyshv >= 0.9.0",
24
23
  "prompt_toolkit",
25
24
  "pygments",
26
25
  "pyxdg",
26
+ "more-itertools",
27
27
  ]
28
28
 
29
29
  [project.urls]
@@ -0,0 +1,55 @@
1
+ """Silicon Heaven Command Line Interface."""
2
+
3
+ import importlib
4
+
5
+ from .__version__ import VERSION
6
+ from .args import register_argparser
7
+ from .builtin import Builtin, Builtins
8
+ from .client import Client
9
+ from .cliitems import CliItems
10
+ from .config import Config, ConfigError, register_config
11
+ from .option import BoolOption, FloatOption, IntOption, Option
12
+ from .options import (
13
+ AutoGetOption,
14
+ AutoGetTimeoutOption,
15
+ AutoProbeOption,
16
+ CallDuration,
17
+ CallQueryTimeout,
18
+ CallRetryTimeout,
19
+ DebugOption,
20
+ RawOption,
21
+ ViModeOption,
22
+ )
23
+ from .state import State, StateVar
24
+ from .tree import Node, Tree
25
+
26
+ __all__ = [
27
+ "VERSION",
28
+ "AutoGetOption",
29
+ "AutoGetTimeoutOption",
30
+ "AutoProbeOption",
31
+ "BoolOption",
32
+ "Builtin",
33
+ "Builtins",
34
+ "CallDuration",
35
+ "CallQueryTimeout",
36
+ "CallRetryTimeout",
37
+ "CliItems",
38
+ "Client",
39
+ "Config",
40
+ "ConfigError",
41
+ "DebugOption",
42
+ "FloatOption",
43
+ "IntOption",
44
+ "Node",
45
+ "Option",
46
+ "RawOption",
47
+ "State",
48
+ "StateVar",
49
+ "Tree",
50
+ "ViModeOption",
51
+ "register_argparser",
52
+ "register_config",
53
+ ]
54
+
55
+ importlib.import_module(".builtins", package=__package__)
@@ -0,0 +1,36 @@
1
+ """Implementation of command line application."""
2
+
3
+ import asyncio
4
+
5
+ from .afterconn import Afterconn
6
+ from .args import args_apply, args_parse
7
+ from .cli import run
8
+ from .client import Client
9
+ from .config import load_config
10
+ from .plugins import load_plugins
11
+ from .state import State
12
+ from .url import Url
13
+
14
+
15
+ def main() -> None:
16
+ """Application's entrypoint."""
17
+ load_plugins()
18
+ argsgens, args = args_parse()
19
+ state = State()
20
+ load_config(args.config, state)
21
+ args_apply(argsgens, args, state)
22
+ state.cache_load(Url(state).cache_path())
23
+
24
+ async def async_main() -> None:
25
+ client = await Client.connect(Url(state).url, state=state)
26
+ await Afterconn(state).run(client)
27
+ await run(client)
28
+ if client.client.connected:
29
+ await client.disconnect()
30
+
31
+ asyncio.run(async_main())
32
+ state.cache_dump(Url(state).cache_path())
33
+
34
+
35
+ if __name__ == "__main__":
36
+ main()
@@ -0,0 +1,55 @@
1
+ """The after connection tasks."""
2
+
3
+ import argparse
4
+ import collections.abc
5
+ import typing
6
+
7
+ from .args import ArgsParseFuncGenT, register_argparser
8
+ from .client import Client, SHVPath
9
+ from .cliitems import CliItems
10
+ from .scan import scan_nodes
11
+ from .state import StateVar
12
+
13
+ AfterConnT: typing.TypeAlias = collections.abc.Callable[
14
+ [Client], collections.abc.Coroutine[None, None, None]
15
+ ]
16
+
17
+
18
+ class Afterconn(StateVar, list[AfterConnT]):
19
+ """The set of operations to be performed right after connection."""
20
+
21
+ async def run(self, client: Client) -> None:
22
+ """Run the steps setup to be performed after connection at startup."""
23
+ for func in self:
24
+ await func(client)
25
+
26
+
27
+ @register_argparser
28
+ def _argparser(parser: argparse.ArgumentParser) -> ArgsParseFuncGenT:
29
+ parser.add_argument(
30
+ "-s",
31
+ "--subscribe",
32
+ action="append",
33
+ default=[],
34
+ help="Automatic subscription right after connection.",
35
+ )
36
+ parser.add_argument(
37
+ "--scan",
38
+ action="store_true",
39
+ help="Perform scan as a first action right after connect.",
40
+ )
41
+ parser.add_argument(
42
+ "--scan-depth",
43
+ type=int,
44
+ default=3,
45
+ help="Depth of the scan performed if --scan is used.",
46
+ )
47
+ args, state = yield
48
+
49
+ async def afterconn(client: Client) -> None:
50
+ for ri in args.subscribe:
51
+ await client.subscribe(CliItems.extend_ri(ri))
52
+ if args.scan:
53
+ await scan_nodes(client, SHVPath(""), args.scan_dept)
54
+
55
+ Afterconn(state).append(afterconn)
@@ -0,0 +1,56 @@
1
+ """Argument parser wrapper for the SHVCLI."""
2
+
3
+ import argparse
4
+ import collections.abc
5
+ import contextlib
6
+ import pathlib
7
+ import typing
8
+
9
+ from .__version__ import VERSION
10
+ from .state import State
11
+
12
+ ArgsParseFuncGenT: typing.TypeAlias = collections.abc.Generator[
13
+ None, tuple[argparse.Namespace, State], None
14
+ ]
15
+ ArgsparseFuncT: typing.TypeAlias = collections.abc.Callable[
16
+ [argparse.ArgumentParser], ArgsParseFuncGenT
17
+ ]
18
+
19
+ registered_functions: list[ArgsparseFuncT] = []
20
+
21
+
22
+ def register_argparser(func: ArgsparseFuncT) -> None:
23
+ """Decorate function to register it to be called when arguments are being parsed."""
24
+ registered_functions.append(func)
25
+
26
+
27
+ def args_parse() -> tuple[tuple[ArgsParseFuncGenT, ...], argparse.Namespace]:
28
+ """Parse passed arguments and return result."""
29
+ parser = argparse.ArgumentParser(prog="shvcli", description="Silicon Heaven CLI")
30
+ parser.add_argument("--version", action="version", version="%(prog)s " + VERSION)
31
+ parser.add_argument(
32
+ "-c",
33
+ "--config",
34
+ action="append",
35
+ type=pathlib.Path,
36
+ default=[pathlib.Path("/etc/shvcli.toml"), pathlib.Path("~/.shvcli.toml")],
37
+ )
38
+ gens = tuple(func(parser) for func in registered_functions)
39
+ for gen in gens:
40
+ next(gen)
41
+ args = parser.parse_args()
42
+ return gens, args
43
+
44
+
45
+ def args_apply(
46
+ gens: tuple[ArgsParseFuncGenT, ...], args: argparse.Namespace, state: State
47
+ ) -> None:
48
+ """Apply parsed arguments on the given state.
49
+
50
+ The parsed arguments should be applied after configuration but
51
+ configuration loading needs to be influenced by arguments and thus split
52
+ arguments parsing to the parsing and application phase.
53
+ """
54
+ for gen in gens:
55
+ with contextlib.suppress(StopIteration):
56
+ gen.send((args, state))
@@ -0,0 +1,86 @@
1
+ """Builtin methods glue code so they can be defined separatelly."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import abc
6
+ import collections.abc
7
+ import inspect
8
+ import typing
9
+
10
+ from prompt_toolkit.completion import Completion
11
+
12
+ from .client import Client
13
+ from .cliitems import CliItems
14
+ from .state import State, StateVar
15
+
16
+
17
+ class Builtin(abc.ABC):
18
+ """The single builtin definition."""
19
+
20
+ builtins: typing.Final[list[type[Builtin]]] = []
21
+
22
+ def __init_subclass__(cls, *args: typing.Any, **kwargs: typing.Any) -> None: # noqa: ANN401
23
+ super().__init_subclass__(*args, **kwargs)
24
+ Builtin.builtins.append(cls)
25
+
26
+ def __init__(self, builtins: Builtins, state: State) -> None:
27
+ self.state = state
28
+
29
+ @property
30
+ @abc.abstractmethod
31
+ def description(self) -> tuple[str, str]:
32
+ """The provider of the description of this builtin.
33
+
34
+ :return: The first string is description of arguments expected by
35
+ builtin and the second one is the builtin description.
36
+ """
37
+
38
+ def completion( # noqa: PLR6301
39
+ self, items: CliItems, client: Client
40
+ ) -> collections.abc.Iterable[Completion]:
41
+ """Parameter completion for this builtin."""
42
+ return tuple()
43
+
44
+ async def completion_async( # noqa: PLR6301
45
+ self, items: CliItems, client: Client
46
+ ) -> collections.abc.AsyncGenerator[Completion, None]:
47
+ """Asynchronous parameter completion for this builtin.
48
+
49
+ This is used only if :class:`AutoProbeOption` is enabled.
50
+
51
+ It is common that this only fetches the required data with client and
52
+ the completion is performed in :meth:`completion` only.
53
+ """
54
+ return
55
+ yield # type: ignore
56
+
57
+ def validate(self, items: CliItems, client: Client) -> None: # noqa: PLR6301
58
+ """Validate the parameter for this builtin."""
59
+ return
60
+
61
+ async def validate_async(self, items: CliItems, client: Client) -> None: # noqa: PLR6301
62
+ """Asynchronously validate the parameter for this builtin.
63
+
64
+ This is used only if :class:`AutoProbeOption` is enabled.
65
+
66
+ It is common that this only fetches the required data with client and
67
+ the validation is performed in :meth:`validate` only. SHVCLI also
68
+ always uses completion and validation together and because completion
69
+ happens alongside with validation it is rather common to do this
70
+ probing in the :meth:`completion_async` only.
71
+ """
72
+ return
73
+
74
+ @abc.abstractmethod
75
+ async def run(self, items: CliItems, client: Client) -> None:
76
+ """Run the builtin."""
77
+
78
+
79
+ class Builtins(StateVar, dict[str, Builtin]):
80
+ """All initialized builtins."""
81
+
82
+ def __init__(self, state: State) -> None:
83
+ super().__init__(state)
84
+ for builtin in Builtin.builtins:
85
+ if not inspect.isabstract(builtin):
86
+ builtin(self, state)
@@ -0,0 +1,27 @@
1
+ """All standard builtins.
2
+
3
+ This module is imported in the top level init and thus all builtins imported
4
+ here are automatically registered.
5
+ """
6
+
7
+ from .broker import BuiltinSubscribe, BuiltinSubscriptions, BuiltinUnsubscribe
8
+ from .cd import BuiltinCD
9
+ from .file import BuiltinDownload, BuiltinUpload, BuiltinVerify
10
+ from .help import BuiltinHelp
11
+ from .scan import BuiltinScan
12
+ from .set import BuiltinSet
13
+ from .tree import BuiltinTree
14
+
15
+ __all__ = [
16
+ "BuiltinCD",
17
+ "BuiltinDownload",
18
+ "BuiltinHelp",
19
+ "BuiltinScan",
20
+ "BuiltinSet",
21
+ "BuiltinSubscribe",
22
+ "BuiltinSubscriptions",
23
+ "BuiltinTree",
24
+ "BuiltinUnsubscribe",
25
+ "BuiltinUpload",
26
+ "BuiltinVerify",
27
+ ]
@@ -0,0 +1,100 @@
1
+ """Broker specific builtins."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import collections.abc
6
+
7
+ from prompt_toolkit.completion import Completion
8
+ from prompt_toolkit.validation import ValidationError
9
+
10
+ from ..builtin import Builtin, Builtins
11
+ from ..client import Client
12
+ from ..cliitems import CliItems
13
+ from ..state import State
14
+ from ..tools.complet import comp_from, comp_signal_ri
15
+ from ..tools.print import print_flist
16
+ from ..tree import Tree
17
+
18
+
19
+ class BuiltinSubscribe(Builtin):
20
+ """The implementation of ``subscribe`` builtin."""
21
+
22
+ def __init__(self, builtins: Builtins, state: State) -> None:
23
+ super().__init__(builtins, state)
24
+ builtins["subscribe"] = self
25
+ builtins["sub"] = self
26
+
27
+ @property
28
+ def description(self) -> tuple[str, str]: # noqa: D102
29
+ return "[PATH:METHOD:SIGNAL]...", "Subscribe to given RPC RIs."
30
+
31
+ def completion( # noqa: D102
32
+ self, items: CliItems, client: Client
33
+ ) -> collections.abc.Iterable[Completion]:
34
+ yield from comp_signal_ri(
35
+ items.param.rpartition(" ")[2], self.state.path, Tree(self.state)
36
+ )
37
+
38
+ async def completion_async( # noqa: D102
39
+ self, items: CliItems, client: Client
40
+ ) -> collections.abc.AsyncGenerator[Completion, None]:
41
+ last = items.param.rpartition(" ")[2]
42
+ if ":" not in last:
43
+ path = items.path / last
44
+ await client.probe(path if last.endswith("/") else path.parent)
45
+
46
+ async for res in super().completion_async(items, client):
47
+ yield res
48
+
49
+ async def run(self, items: CliItems, client: Client) -> None: # noqa: PLR6301, D102
50
+ for ri in items.ri_param:
51
+ await client.subscribe(ri)
52
+
53
+
54
+ class BuiltinUnsubscribe(Builtin):
55
+ """The implementation of ``unsubscribe`` builtin."""
56
+
57
+ def __init__(self, builtins: Builtins, state: State) -> None:
58
+ super().__init__(builtins, state)
59
+ builtins["unsubscribe"] = self
60
+ builtins["usub"] = self
61
+
62
+ @property
63
+ def description(self) -> tuple[str, str]: # noqa: D102
64
+ return "[PATH:METHOD:SIGNAL]...", "Unsubscribe to given or all RPC RIs."
65
+
66
+ def completion( # noqa: PLR6301, D102
67
+ self, items: CliItems, client: Client
68
+ ) -> collections.abc.Iterable[Completion]:
69
+ yield from comp_from(items.param.rpartition(" ")[2], client.subscriptions())
70
+
71
+ def validate(self, items: CliItems, client: Client) -> None: # noqa: PLR6301, D102
72
+ if missing := set(items.param.split()) - set(client.subscriptions()):
73
+ raise ValidationError(message=f"Not subscribed: {' '.join(missing)}")
74
+
75
+ async def run(self, items: CliItems, client: Client) -> None: # noqa: PLR6301, D102
76
+ for ri in items.ri_param if items.param else client.subscriptions():
77
+ await client.unsubscribe(ri)
78
+
79
+
80
+ class BuiltinSubscriptions(Builtin):
81
+ """The implementation of ``subscriptions`` builtin."""
82
+
83
+ def __init__(self, builtins: Builtins, state: State) -> None:
84
+ super().__init__(builtins, state)
85
+ builtins["subscriptions"] = self
86
+ builtins["subs"] = self
87
+
88
+ @property
89
+ def description(self) -> tuple[str, str]: # noqa: D102
90
+ return "", "Display current subscriptions."
91
+
92
+ async def run(self, items: CliItems, client: Client) -> None: # noqa: PLR6301, D102
93
+ subs = await client.call(".broker/currentClient", "subscriptions")
94
+ if isinstance(subs, dict):
95
+ print_flist(
96
+ ("", sub + (f"?{timeout}secs" if timeout is not None else ""))
97
+ for sub, timeout in subs.items()
98
+ )
99
+ else:
100
+ print(subs)
@@ -0,0 +1,37 @@
1
+ """The cd builtin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import collections.abc
6
+
7
+ from prompt_toolkit.completion import Completion
8
+
9
+ from ..builtin import Builtin, Builtins
10
+ from ..client import Client
11
+ from ..cliitems import CliItems
12
+ from ..state import State
13
+ from ..tools.complet import comp_path
14
+ from ..tree import Tree
15
+
16
+
17
+ class BuiltinCD(Builtin):
18
+ """The implementation of ``cd`` builtin that allows change to invalid path."""
19
+
20
+ def __init__(self, builtins: Builtins, state: State) -> None:
21
+ super().__init__(builtins, state)
22
+ builtins["cd"] = self
23
+
24
+ @property
25
+ def description(self) -> tuple[str, str]: # noqa: D102
26
+ return "[PATH]", "Change to given path even if it is invalid."
27
+
28
+ def completion( # noqa: D102
29
+ self, items: CliItems, client: Client
30
+ ) -> collections.abc.Iterable[Completion]:
31
+ yield from comp_path(items.param, self.state.path, Tree(self.state), "")
32
+
33
+ async def run(self, items: CliItems, client: Client) -> None: # noqa: D102
34
+ try:
35
+ self.state.path = items.path_param
36
+ except ValueError as exc:
37
+ print(exc)