shvcli 0.3.0__tar.gz → 0.5.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.3.0/shvcli.egg-info → shvcli-0.5.0}/PKG-INFO +7 -12
- {shvcli-0.3.0 → shvcli-0.5.0}/README.rst +3 -3
- shvcli-0.5.0/pyproject.toml +101 -0
- shvcli-0.5.0/shvcli/__init__.py +5 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/__main__.py +1 -0
- shvcli-0.5.0/shvcli/__version__.py +26 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/builtin.py +8 -2
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/builtin_impl.py +15 -17
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/cli.py +5 -4
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/client.py +36 -28
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/complet.py +5 -3
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/complet_tools.py +1 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/config.py +3 -2
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/lsdir.py +11 -7
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/parse.py +14 -22
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/scan.py +1 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/tools.py +4 -3
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/valid.py +3 -2
- {shvcli-0.3.0 → shvcli-0.5.0/shvcli.egg-info}/PKG-INFO +7 -12
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli.egg-info/SOURCES.txt +1 -0
- shvcli-0.5.0/shvcli.egg-info/requires.txt +4 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli.egg-info/top_level.txt +1 -0
- shvcli-0.3.0/pyproject.toml +0 -89
- shvcli-0.3.0/shvcli/__init__.py +0 -5
- shvcli-0.3.0/shvcli.egg-info/requires.txt +0 -10
- {shvcli-0.3.0 → shvcli-0.5.0}/LICENSE +0 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/setup.cfg +0 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/py.typed +0 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli/version +0 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli.egg-info/dependency_links.txt +0 -0
- {shvcli-0.3.0 → shvcli-0.5.0}/shvcli.egg-info/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: shvcli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Silicon Heaven CLI access application
|
|
5
5
|
Author: Elektroline a.s.
|
|
6
6
|
License: MIT License
|
|
@@ -25,7 +25,7 @@ License: MIT License
|
|
|
25
25
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
26
|
SOFTWARE.
|
|
27
27
|
|
|
28
|
-
Project-URL: repository, https://gitlab.com/
|
|
28
|
+
Project-URL: repository, https://gitlab.com/silicon-heaven/shvcli
|
|
29
29
|
Classifier: Development Status :: 4 - Beta
|
|
30
30
|
Classifier: License :: OSI Approved :: MIT License
|
|
31
31
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -38,27 +38,22 @@ 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.0
|
|
42
42
|
Requires-Dist: prompt_toolkit
|
|
43
|
+
Requires-Dist: pygments
|
|
43
44
|
Requires-Dist: pyxdg
|
|
44
|
-
Provides-Extra: lint
|
|
45
|
-
Requires-Dist: black; extra == "lint"
|
|
46
|
-
Requires-Dist: isort; extra == "lint"
|
|
47
|
-
Requires-Dist: mypy; extra == "lint"
|
|
48
|
-
Requires-Dist: pydocstyle; extra == "lint"
|
|
49
|
-
Requires-Dist: pylint; extra == "lint"
|
|
50
45
|
|
|
51
46
|
=====================================
|
|
52
47
|
Silicon Heaven CLI access application
|
|
53
48
|
=====================================
|
|
54
|
-
.. image:: https://gitlab.com/
|
|
49
|
+
.. image:: https://gitlab.com/silicon-heaven/shvcli/-/raw/master/logo.svg
|
|
55
50
|
:align: right
|
|
56
51
|
:height: 128px
|
|
57
52
|
|
|
58
53
|
This provides an easy to use CLI interfase to access the SHV network.
|
|
59
54
|
|
|
60
|
-
* `📃 Sources <https://gitlab.com/
|
|
61
|
-
* `⁉️ Issue tracker <https://gitlab.com/
|
|
55
|
+
* `📃 Sources <https://gitlab.com/silicon-heaven/shvcli>`__
|
|
56
|
+
* `⁉️ Issue tracker <https://gitlab.com/silicon-heaven/shvcli/-/issues>`__
|
|
62
57
|
* `📕 Silicon Heaven protocol documentation <https://silicon-heaven.github.io/shv-doc/>`__
|
|
63
58
|
|
|
64
59
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
=====================================
|
|
2
2
|
Silicon Heaven CLI access application
|
|
3
3
|
=====================================
|
|
4
|
-
.. image:: https://gitlab.com/
|
|
4
|
+
.. image:: https://gitlab.com/silicon-heaven/shvcli/-/raw/master/logo.svg
|
|
5
5
|
:align: right
|
|
6
6
|
:height: 128px
|
|
7
7
|
|
|
8
8
|
This provides an easy to use CLI interfase to access the SHV network.
|
|
9
9
|
|
|
10
|
-
* `📃 Sources <https://gitlab.com/
|
|
11
|
-
* `⁉️ Issue tracker <https://gitlab.com/
|
|
10
|
+
* `📃 Sources <https://gitlab.com/silicon-heaven/shvcli>`__
|
|
11
|
+
* `⁉️ Issue tracker <https://gitlab.com/silicon-heaven/shvcli/-/issues>`__
|
|
12
12
|
* `📕 Silicon Heaven protocol documentation <https://silicon-heaven.github.io/shv-doc/>`__
|
|
13
13
|
|
|
14
14
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "shvcli"
|
|
3
|
+
version = "0.5.0"
|
|
4
|
+
description = "Silicon Heaven CLI access application"
|
|
5
|
+
readme = "README.rst"
|
|
6
|
+
license = {file = "LICENSE"}
|
|
7
|
+
authors = [
|
|
8
|
+
{ name="Elektroline a.s." },
|
|
9
|
+
]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"License :: OSI Approved :: MIT License",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: End Users/Desktop",
|
|
17
|
+
"Topic :: Home Automation",
|
|
18
|
+
"Topic :: System :: Shells",
|
|
19
|
+
"Topic :: Terminals",
|
|
20
|
+
]
|
|
21
|
+
requires-python = ">=3.11"
|
|
22
|
+
dependencies = [
|
|
23
|
+
"pyshv >= 0.7.0",
|
|
24
|
+
"prompt_toolkit",
|
|
25
|
+
"pygments",
|
|
26
|
+
"pyxdg",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
repository = "https://gitlab.com/silicon-heaven/shvcli"
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
shvcli = "shvcli.__main__:main"
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["setuptools"]
|
|
37
|
+
build-backend = "setuptools.build_meta"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
[tool.setuptools.package-data]
|
|
41
|
+
"*" = ["py.typed"]
|
|
42
|
+
|
|
43
|
+
[tool.coverage.run]
|
|
44
|
+
branch = true
|
|
45
|
+
[tool.coverage.report]
|
|
46
|
+
exclude_lines = [
|
|
47
|
+
"pragma: no cover",
|
|
48
|
+
"if typing.TYPE_CHECKING:"
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
target-version = "py311"
|
|
53
|
+
[tool.ruff.lint]
|
|
54
|
+
select = [
|
|
55
|
+
"F", "E", "W", "I", "N", "D", "UP", "YTT", "ANN", "ASYNC", "ASYNC1", "S", "BLE",
|
|
56
|
+
"B", "A", "COM", "PL", "RUF"
|
|
57
|
+
]
|
|
58
|
+
ignore = [
|
|
59
|
+
# Magic methods have defined behavior so what is there to document. __init__
|
|
60
|
+
# should be documented in class docstring.
|
|
61
|
+
"D105", "D107",
|
|
62
|
+
# These have conflicting rules.
|
|
63
|
+
"D203", "D213",
|
|
64
|
+
# Ignore too long lines (format tackles that)
|
|
65
|
+
"E501",
|
|
66
|
+
# It is nice that assert should not be used for input checking but it can be
|
|
67
|
+
# also used for internal consistency checking.
|
|
68
|
+
"S101", "B011",
|
|
69
|
+
# subprocess can be insecurely used but not is insecure by design
|
|
70
|
+
"S404", "S603", "S607",
|
|
71
|
+
# Catching Exception is valid and safe in some cases and thus should be allowed
|
|
72
|
+
"BLE001",
|
|
73
|
+
# Conflict with formatter
|
|
74
|
+
"COM812",
|
|
75
|
+
# Remove too-many limit for more complex code.
|
|
76
|
+
"PLR0904", "PLR0911", "PLR0912", "PLR0913", "PLR0917", "PLR2004", "PLR1702",
|
|
77
|
+
"PLR0916",
|
|
78
|
+
# This seems like a wrong rule
|
|
79
|
+
"PLW1641",
|
|
80
|
+
# Deprecated
|
|
81
|
+
"ANN101", "ANN102",
|
|
82
|
+
]
|
|
83
|
+
preview = true
|
|
84
|
+
[tool.ruff.lint.per-file-ignores]
|
|
85
|
+
"tests/**" = [
|
|
86
|
+
"D100", "D101", "D102", "D103", "D104", "S101", "S106", "ANN001", "ANN002",
|
|
87
|
+
"ANN003", "ANN201", "ANN202", "ANN204", "ANN205", "PLR6301",
|
|
88
|
+
]
|
|
89
|
+
"docs/conf.py" = ["D100", "D103", "A001", "ANN201", "ANN001"]
|
|
90
|
+
[tool.ruff.format]
|
|
91
|
+
docstring-code-format = true
|
|
92
|
+
preview = true
|
|
93
|
+
|
|
94
|
+
[tool.mypy]
|
|
95
|
+
python_version = "3.11"
|
|
96
|
+
ignore_missing_imports = true
|
|
97
|
+
disallow_untyped_defs = true
|
|
98
|
+
warn_redundant_casts = true
|
|
99
|
+
warn_unused_ignores = true
|
|
100
|
+
warn_return_any = true
|
|
101
|
+
warn_unreachable = true
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""The smarter loader of the version.
|
|
2
|
+
|
|
3
|
+
It identifies if this package is installed and returns version from egg info or
|
|
4
|
+
it looks for pyproject.toml file in upper directory.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib.metadata
|
|
8
|
+
import pathlib
|
|
9
|
+
import tomllib
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_version() -> str:
|
|
13
|
+
for dist_name in importlib.metadata.packages_distributions().get(__package__, []):
|
|
14
|
+
dist = importlib.metadata.distribution(dist_name)
|
|
15
|
+
if str(dist.locate_file(f"{__package__}/__version__.py")) == __file__:
|
|
16
|
+
return dist.version
|
|
17
|
+
pyproject = pathlib.Path(__file__).parent.parent / "pyproject.toml"
|
|
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
|
|
23
|
+
return "unknown"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
VERSION = _get_version()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Builtin methods glue code so they can be defined separatelly."""
|
|
2
|
+
|
|
2
3
|
import collections.abc
|
|
3
4
|
import dataclasses
|
|
4
5
|
import typing
|
|
@@ -9,7 +10,9 @@ from .client import SHVClient
|
|
|
9
10
|
from .config import CliConfig
|
|
10
11
|
from .parse import CliItems
|
|
11
12
|
|
|
12
|
-
_T_METHOD = typing.Callable[
|
|
13
|
+
_T_METHOD = typing.Callable[
|
|
14
|
+
[SHVClient, CliConfig, CliItems], typing.Awaitable[None] | None
|
|
15
|
+
]
|
|
13
16
|
_T_XMETHOD = typing.Callable[
|
|
14
17
|
[SHVClient, CliConfig, CliItems, int | None], typing.Awaitable[None]
|
|
15
18
|
]
|
|
@@ -114,7 +117,10 @@ async def call_builtin(
|
|
|
114
117
|
return
|
|
115
118
|
|
|
116
119
|
if isinstance(m, Method):
|
|
117
|
-
|
|
120
|
+
res = m.func(shvclient, config, items)
|
|
121
|
+
if isinstance(res, collections.abc.Awaitable):
|
|
122
|
+
await res
|
|
123
|
+
return
|
|
118
124
|
|
|
119
125
|
typing.assert_type(m, XMethod)
|
|
120
126
|
strnum = items.method[len(m.name) + 1 :]
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Implementations of our builtin methods."""
|
|
2
|
+
|
|
2
3
|
import collections.abc
|
|
3
4
|
import inspect
|
|
4
5
|
import itertools
|
|
5
6
|
|
|
6
7
|
from prompt_toolkit.completion import Completion
|
|
7
|
-
from shv import RpcSubscription
|
|
8
8
|
|
|
9
9
|
from .builtin import METHODS, Argument, XMethod, builtin, xbuiltin
|
|
10
10
|
from .client import Node, SHVClient
|
|
@@ -21,11 +21,13 @@ 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 = items.
|
|
25
|
-
assert method is not None
|
|
24
|
+
path, method, signal = items.interpret_param_ri(config).split(":")
|
|
26
25
|
node = shvclient.tree.get_path(path)
|
|
27
26
|
if node is not None:
|
|
28
|
-
|
|
27
|
+
if items.param_raw.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])
|
|
29
31
|
else:
|
|
30
32
|
yield from comp_path(shvclient, config, items)
|
|
31
33
|
|
|
@@ -52,7 +54,7 @@ argument_set = Argument("[OPTION [VALUE]]", argument_set_comp)
|
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
@builtin("help", {"h"}, hidden=True)
|
|
55
|
-
|
|
57
|
+
def _help(_: SHVClient, __: CliConfig, ___: CliItems) -> None:
|
|
56
58
|
"""Print help for all builtin methods."""
|
|
57
59
|
print_row("Available internal methods (all prefixed with '!'):")
|
|
58
60
|
for name, m in METHODS.items():
|
|
@@ -69,15 +71,13 @@ async def _help(_: SHVClient, __: CliConfig, ___: CliItems) -> None:
|
|
|
69
71
|
@builtin(aliases={"sub"}, argument=argument_signal)
|
|
70
72
|
async def subscribe(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
71
73
|
"""Add new subscribe."""
|
|
72
|
-
|
|
73
|
-
await shvclient.subscribe(RpcSubscription(path, method or ""))
|
|
74
|
+
await shvclient.subscribe(items.interpret_param_ri(config))
|
|
74
75
|
|
|
75
76
|
|
|
76
77
|
@builtin(aliases={"usub"}, argument=argument_signal)
|
|
77
78
|
async def unsubscribe(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
78
79
|
"""Unsubscribe existing subscription."""
|
|
79
|
-
|
|
80
|
-
await shvclient.unsubscribe(RpcSubscription(path, method or ""))
|
|
80
|
+
await shvclient.unsubscribe(items.interpret_param_ri(config))
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
@builtin(aliases={"subs", "test"})
|
|
@@ -87,7 +87,7 @@ async def subscriptions(shvclient: SHVClient, _: CliConfig, __: CliItems) -> Non
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
@builtin(aliases={"t"}, argument=argument_path)
|
|
90
|
-
|
|
90
|
+
def tree(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
91
91
|
"""Print tree of nodes discovered in this session."""
|
|
92
92
|
|
|
93
93
|
def print_node(node: Node, cols: list[bool]) -> None:
|
|
@@ -95,12 +95,10 @@ async def tree(shvclient: SHVClient, config: CliConfig, items: CliItems) -> None
|
|
|
95
95
|
print_ftext(
|
|
96
96
|
itertools.chain(
|
|
97
97
|
(("", "│ " if c else " ") for c in cols),
|
|
98
|
-
iter(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
]
|
|
103
|
-
),
|
|
98
|
+
iter([
|
|
99
|
+
("", "├─" if hasnext else "└─"),
|
|
100
|
+
ls_node_format(name, parent_node=node),
|
|
101
|
+
]),
|
|
104
102
|
)
|
|
105
103
|
)
|
|
106
104
|
print_node(node[name], [*cols, hasnext])
|
|
@@ -127,7 +125,7 @@ async def scan(
|
|
|
127
125
|
|
|
128
126
|
|
|
129
127
|
@builtin("set", aliases={"s"}, argument=argument_set)
|
|
130
|
-
|
|
128
|
+
def _set(_: SHVClient, config: CliConfig, items: CliItems) -> None:
|
|
131
129
|
"""Set configuration options in runtime."""
|
|
132
130
|
opt, val = items.interpret_param_set()
|
|
133
131
|
if not opt:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Command line interface."""
|
|
2
|
+
|
|
2
3
|
import asyncio
|
|
3
4
|
import json
|
|
4
5
|
import pathlib
|
|
@@ -8,9 +9,9 @@ import xdg.BaseDirectory
|
|
|
8
9
|
from prompt_toolkit import PromptSession
|
|
9
10
|
from prompt_toolkit.history import FileHistory
|
|
10
11
|
from prompt_toolkit.patch_stdout import patch_stdout
|
|
11
|
-
from shv import RpcError, RpcUrl
|
|
12
|
+
from shv import RpcError, RpcLogin, RpcUrl
|
|
12
13
|
|
|
13
|
-
from . import builtin_impl as _
|
|
14
|
+
from . import builtin_impl as _ # noqa F401
|
|
14
15
|
from .builtin import call_builtin
|
|
15
16
|
from .client import Node, SHVClient
|
|
16
17
|
from .complet import CliCompleter
|
|
@@ -51,7 +52,7 @@ async def _app(config: CliConfig, shvclient: SHVClient) -> None:
|
|
|
51
52
|
[prompt_path, ("", "> ")], vi_mode=config.vimode
|
|
52
53
|
)
|
|
53
54
|
except EOFError:
|
|
54
|
-
shvclient.
|
|
55
|
+
await shvclient.disconnect()
|
|
55
56
|
return
|
|
56
57
|
await handle_line(shvclient, config, result)
|
|
57
58
|
except KeyboardInterrupt:
|
|
@@ -68,7 +69,7 @@ async def run(config: CliConfig) -> None:
|
|
|
68
69
|
location=config.url.location,
|
|
69
70
|
port=config.url.port,
|
|
70
71
|
protocol=config.url.protocol,
|
|
71
|
-
username=config.url.username,
|
|
72
|
+
login=RpcLogin(username=config.url.login.username),
|
|
72
73
|
)
|
|
73
74
|
cpath = xdg.BaseDirectory.save_cache_path("shvcli")
|
|
74
75
|
fname = re.sub(r"[^\w_. -]", "_", cacheurl.to_url())
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""SHV client for shvcli."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import collections.abc
|
|
@@ -6,7 +7,6 @@ import typing
|
|
|
6
7
|
from pathlib import PurePosixPath
|
|
7
8
|
|
|
8
9
|
from shv import (
|
|
9
|
-
RpcClient,
|
|
10
10
|
RpcError,
|
|
11
11
|
RpcMessage,
|
|
12
12
|
RpcMethodDesc,
|
|
@@ -26,12 +26,11 @@ 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
|
|
|
34
|
-
def __getitem__(self, key: str) ->
|
|
33
|
+
def __getitem__(self, key: str) -> Node:
|
|
35
34
|
"""Get node from nodes."""
|
|
36
35
|
return self.nodes[key]
|
|
37
36
|
|
|
@@ -43,7 +42,7 @@ class Node(collections.abc.Mapping[str, "Node"]):
|
|
|
43
42
|
"""Get number of child nodes."""
|
|
44
43
|
return len(self.nodes)
|
|
45
44
|
|
|
46
|
-
def valid_path(self, path: str | PurePosixPath) ->
|
|
45
|
+
def valid_path(self, path: str | PurePosixPath) -> Node:
|
|
47
46
|
"""Add valid path relative to this node."""
|
|
48
47
|
if isinstance(path, str):
|
|
49
48
|
path = PurePosixPath(path)
|
|
@@ -68,7 +67,7 @@ class Node(collections.abc.Mapping[str, "Node"]):
|
|
|
68
67
|
if pnode is not None:
|
|
69
68
|
pnode.nodes.pop(path.name)
|
|
70
69
|
|
|
71
|
-
def get_path(self, path: str | PurePosixPath) ->
|
|
70
|
+
def get_path(self, path: str | PurePosixPath) -> None | Node:
|
|
72
71
|
"""Get node on given path."""
|
|
73
72
|
if isinstance(path, str):
|
|
74
73
|
path = PurePosixPath(path)
|
|
@@ -79,26 +78,26 @@ class Node(collections.abc.Mapping[str, "Node"]):
|
|
|
79
78
|
node = node[n]
|
|
80
79
|
return node
|
|
81
80
|
|
|
82
|
-
def dump(self) ->
|
|
81
|
+
def dump(self) -> dict[str, object]:
|
|
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
|
}
|
|
91
89
|
|
|
92
90
|
@classmethod
|
|
93
|
-
def load(cls, data:
|
|
91
|
+
def load(cls, data: collections.abc.Mapping[str, object]) -> Node:
|
|
94
92
|
"""Load node tree from dump."""
|
|
95
93
|
assert isinstance(data, collections.abc.Mapping)
|
|
96
94
|
res = cls()
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
res.
|
|
95
|
+
if isinstance(data["nodes"], collections.abc.Mapping):
|
|
96
|
+
res.nodes = {n: cls.load(v) for n, v in data["nodes"].items()}
|
|
97
|
+
if isinstance(data["methods"], collections.abc.Mapping):
|
|
98
|
+
res.methods = {str(n): set(v) for n, v in data["methods"].items()}
|
|
99
|
+
res.nodes_probed = bool(data["nodes_probed"])
|
|
100
|
+
res.methods_probed = bool(data["methods_probed"])
|
|
102
101
|
return res
|
|
103
102
|
|
|
104
103
|
|
|
@@ -108,11 +107,11 @@ class SHVClient(SimpleClient):
|
|
|
108
107
|
APP_NAME = "shvcli"
|
|
109
108
|
APP_VERSION = VERSION
|
|
110
109
|
|
|
111
|
-
def __init__(self,
|
|
110
|
+
def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None: # noqa ANN401
|
|
112
111
|
"""Initialize client and set create reference to tree."""
|
|
113
112
|
self.tree = Node()
|
|
114
113
|
self.tree.valid_path(".app")
|
|
115
|
-
super().__init__(
|
|
114
|
+
super().__init__(*args, **kwargs)
|
|
116
115
|
|
|
117
116
|
async def _loop(self) -> None:
|
|
118
117
|
await super()._loop()
|
|
@@ -121,10 +120,10 @@ class SHVClient(SimpleClient):
|
|
|
121
120
|
async def _message(self, msg: RpcMessage) -> None:
|
|
122
121
|
await super()._message(msg)
|
|
123
122
|
if msg.is_signal:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
print(f"{msg.path}:{
|
|
123
|
+
self.tree.valid_path(msg.path or "").methods.setdefault(
|
|
124
|
+
msg.source, set()
|
|
125
|
+
).add(msg.signal_name)
|
|
126
|
+
print(f"{msg.path}:{msg.source}:{msg.signal_name}: {Cpon.pack(msg.param)}")
|
|
128
127
|
|
|
129
128
|
async def ls(self, path: str) -> list[str]:
|
|
130
129
|
"""List same as in ValueClient but with result being preserved in tree."""
|
|
@@ -150,25 +149,34 @@ class SHVClient(SimpleClient):
|
|
|
150
149
|
self.tree.invalid_path(path)
|
|
151
150
|
raise exc
|
|
152
151
|
node = self.tree.valid_path(path)
|
|
153
|
-
node.methods = {
|
|
154
|
-
|
|
152
|
+
node.methods = {
|
|
153
|
+
d.name: set(d.signals)
|
|
154
|
+
for d in res
|
|
155
|
+
if RpcMethodFlags.NOT_CALLABLE not in d.flags
|
|
156
|
+
}
|
|
155
157
|
node.methods_probed = True
|
|
156
158
|
return res
|
|
157
159
|
|
|
158
|
-
async def call(
|
|
160
|
+
async def call(
|
|
161
|
+
self,
|
|
162
|
+
path: str,
|
|
163
|
+
method: str,
|
|
164
|
+
*args: typing.Any, # noqa ANN401
|
|
165
|
+
**kwargs: typing.Any, # noqa ANN401
|
|
166
|
+
) -> SHVType:
|
|
159
167
|
"""Perform call same as ValueClient.
|
|
160
168
|
|
|
161
169
|
This uses every call to get insite into existence of method and node and records
|
|
162
170
|
that in the tree.
|
|
163
171
|
"""
|
|
164
172
|
try:
|
|
165
|
-
res = await super().call(path, method,
|
|
173
|
+
res = await super().call(path, method, *args, **kwargs)
|
|
166
174
|
except RpcMethodNotFoundError as exc:
|
|
167
175
|
raise exc
|
|
168
176
|
except RpcError as exc:
|
|
169
|
-
self.tree.valid_path(path).methods.
|
|
177
|
+
self.tree.valid_path(path).methods.setdefault(method, set())
|
|
170
178
|
raise exc
|
|
171
|
-
self.tree.valid_path(path).methods.
|
|
179
|
+
self.tree.valid_path(path).methods.setdefault(method, set())
|
|
172
180
|
return res
|
|
173
181
|
|
|
174
182
|
async def probe(self, path: str) -> Node | None:
|
|
@@ -190,7 +198,7 @@ class SHVClient(SimpleClient):
|
|
|
190
198
|
async def path_is_valid(self, path: str) -> bool:
|
|
191
199
|
"""Check if given path is valid by using ls command."""
|
|
192
200
|
path = path.strip("/")
|
|
193
|
-
if path
|
|
201
|
+
if not path:
|
|
194
202
|
return True # top level is always valid
|
|
195
203
|
if "/" in path:
|
|
196
204
|
pth, name = path.rsplit("/", maxsplit=1)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Completion for CLI."""
|
|
2
|
+
|
|
2
3
|
import collections.abc
|
|
3
4
|
import typing
|
|
4
5
|
|
|
@@ -28,7 +29,7 @@ class CliCompleter(Completer):
|
|
|
28
29
|
|
|
29
30
|
# Parameters
|
|
30
31
|
if CliFlags.COMPLETE_CALL in items.flags:
|
|
31
|
-
if items.method in
|
|
32
|
+
if items.method in {"ls", "dir"} and not self.config.raw:
|
|
32
33
|
yield from comp_path(self.shvclient, self.config, items)
|
|
33
34
|
elif bmethod := builtin.get_builtin(items.method[1:]):
|
|
34
35
|
if bmethod.argument:
|
|
@@ -58,9 +59,10 @@ class CliCompleter(Completer):
|
|
|
58
59
|
items = parse_line(document.text)
|
|
59
60
|
if self.config.autoprobe and (
|
|
60
61
|
CliFlags.COMPLETE_CALL not in items.flags
|
|
61
|
-
or items.method in
|
|
62
|
+
or items.method in {"ls", "dir"}
|
|
62
63
|
or (
|
|
63
|
-
items.method
|
|
64
|
+
items.method
|
|
65
|
+
and items.method[0] == "!"
|
|
64
66
|
and (bmethod := builtin.get_builtin(items.method[1:])) is not None
|
|
65
67
|
and bmethod.argument
|
|
66
68
|
and bmethod.argument.autoprobe
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Configuration state of the CLI."""
|
|
2
|
+
|
|
2
3
|
import collections.abc
|
|
3
4
|
import configparser
|
|
4
5
|
import enum
|
|
@@ -100,7 +101,7 @@ class CliConfig:
|
|
|
100
101
|
self.__rurl = self.__url
|
|
101
102
|
elif isinstance(self.__url, str):
|
|
102
103
|
self.__rurl = RpcUrl.parse(
|
|
103
|
-
subprocess.run(
|
|
104
|
+
subprocess.run( # noqa S602
|
|
104
105
|
f"printf '%s' \"{self.__url}\"",
|
|
105
106
|
shell=True,
|
|
106
107
|
stdout=subprocess.PIPE,
|
|
@@ -128,7 +129,7 @@ class CliConfig:
|
|
|
128
129
|
return logging.root.level <= logging.DEBUG
|
|
129
130
|
|
|
130
131
|
@debug.setter
|
|
131
|
-
def debug(self, value: bool) -> None:
|
|
132
|
+
def debug(self, value: bool) -> None: # noqa PLR6301
|
|
132
133
|
logging.root.setLevel(logging.DEBUG if value else logging.WARNING)
|
|
133
134
|
|
|
134
135
|
def shvpath(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Special handling of ls and dir methods."""
|
|
2
|
+
|
|
2
3
|
import itertools
|
|
3
4
|
import pathlib
|
|
4
5
|
import string
|
|
@@ -32,14 +33,17 @@ def ls_node_format(
|
|
|
32
33
|
def dir_method_format(method: RpcMethodDesc) -> tuple[str, str]:
|
|
33
34
|
"""Print format for single method info."""
|
|
34
35
|
methstyle = ""
|
|
35
|
-
if
|
|
36
|
-
methstyle = "
|
|
36
|
+
if method.name in {"ls", "dir"}:
|
|
37
|
+
methstyle = "ansibrightblack"
|
|
37
38
|
elif RpcMethodFlags.SETTER in method.flags:
|
|
38
39
|
methstyle = "ansiyellow"
|
|
39
40
|
elif RpcMethodFlags.GETTER in method.flags:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
if method.signals:
|
|
42
|
+
methstyle = "ansimagenta"
|
|
43
|
+
else:
|
|
44
|
+
methstyle = "ansigreen"
|
|
45
|
+
elif method.signals or RpcMethodFlags.NOT_CALLABLE in method.flags:
|
|
46
|
+
methstyle = "ansipurple"
|
|
43
47
|
name = method.name
|
|
44
48
|
return (
|
|
45
49
|
methstyle,
|
|
@@ -74,7 +78,7 @@ async def ls_method(shvclient: SHVClient, config: CliConfig, items: CliItems) ->
|
|
|
74
78
|
else:
|
|
75
79
|
print_row(
|
|
76
80
|
itertools.chain(
|
|
77
|
-
iter(n
|
|
81
|
+
iter([*n, ("", " ")]), cpon_ftext(Cpon.pack(resp))
|
|
78
82
|
)
|
|
79
83
|
)
|
|
80
84
|
continue
|
|
@@ -99,7 +103,7 @@ async def dir_method(shvclient: SHVClient, config: CliConfig, items: CliItems) -
|
|
|
99
103
|
else:
|
|
100
104
|
print_row(
|
|
101
105
|
itertools.chain(
|
|
102
|
-
iter(n
|
|
106
|
+
iter([*n, ("", " ")]), cpon_ftext(Cpon.pack(resp))
|
|
103
107
|
)
|
|
104
108
|
)
|
|
105
109
|
continue
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Implementation of CLI parser."""
|
|
2
|
+
|
|
2
3
|
import dataclasses
|
|
3
4
|
import enum
|
|
4
5
|
|
|
@@ -28,7 +29,7 @@ class CliItems:
|
|
|
28
29
|
param_raw: str = ""
|
|
29
30
|
"""Raw parameter passed from CLI."""
|
|
30
31
|
|
|
31
|
-
flags: CliFlags = CliFlags(0)
|
|
32
|
+
flags: CliFlags = dataclasses.field(default_factory=lambda: CliFlags(0))
|
|
32
33
|
"""Flags signaling the presence of some important dividers."""
|
|
33
34
|
|
|
34
35
|
@property
|
|
@@ -38,16 +39,6 @@ class CliItems:
|
|
|
38
39
|
return None
|
|
39
40
|
return Cpon.unpack(self.param_raw)
|
|
40
41
|
|
|
41
|
-
@property
|
|
42
|
-
def param_method(self) -> tuple[str, str | None]:
|
|
43
|
-
"""Interpret parameter as path and method."""
|
|
44
|
-
if ":" in self.param_raw:
|
|
45
|
-
path, method = self.param_raw.split(":", maxsplit=1)
|
|
46
|
-
return path, method
|
|
47
|
-
if "/" in self.param_raw:
|
|
48
|
-
return self.param_raw, None
|
|
49
|
-
return "", self.param_raw
|
|
50
|
-
|
|
51
42
|
def interpret_param_path(self, config: CliConfig) -> str:
|
|
52
43
|
"""Interpret parameter as path specification.
|
|
53
44
|
|
|
@@ -55,16 +46,18 @@ class CliItems:
|
|
|
55
46
|
"""
|
|
56
47
|
return config.shvpath([self.path, self.param_raw])
|
|
57
48
|
|
|
58
|
-
def
|
|
59
|
-
"""Interpret parameter as
|
|
49
|
+
def interpret_param_ri(self, config: CliConfig, expand: bool = True) -> str:
|
|
50
|
+
"""Interpret parameter as RI.
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
path and method not just parsed :prop:`param_raw`.
|
|
52
|
+
The path in RI is combined with pat in the configuration.
|
|
63
53
|
|
|
64
|
-
:return:
|
|
54
|
+
:return: SHV RPC RI.
|
|
65
55
|
"""
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
parts = self.param_raw.split(":")
|
|
57
|
+
path = parts[0] if parts[0] else "**"
|
|
58
|
+
method = parts[1] if len(parts) == 2 else "*"
|
|
59
|
+
signal = parts[2] if len(parts) == 3 else "*"
|
|
60
|
+
return f"{self.path}{"/" if path else ""}{path}:{method}:{signal}"
|
|
68
61
|
|
|
69
62
|
def interpret_param_set(self) -> tuple[str | None, str | None]:
|
|
70
63
|
"""Interpret parameter as 'set' method specification.
|
|
@@ -84,9 +77,8 @@ def parse_line(line: str) -> CliItems:
|
|
|
84
77
|
if ":" in line:
|
|
85
78
|
res.flags |= CliFlags.HAS_COLON
|
|
86
79
|
res.path, res.method = line.split(":", maxsplit=1)
|
|
80
|
+
elif "/" in line:
|
|
81
|
+
res.path = line
|
|
87
82
|
else:
|
|
88
|
-
|
|
89
|
-
res.path = line
|
|
90
|
-
else:
|
|
91
|
-
res.method = line
|
|
83
|
+
res.method = line
|
|
92
84
|
return res
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Various tools used in the code."""
|
|
2
|
+
|
|
2
3
|
import collections.abc
|
|
3
4
|
import os
|
|
4
5
|
import textwrap
|
|
@@ -39,7 +40,7 @@ def intersperse(what: T, iterin: typing.Iterable[T]) -> typing.Iterator[T]:
|
|
|
39
40
|
|
|
40
41
|
def print_ftext(
|
|
41
42
|
fstrs: collections.abc.Iterable[tuple[str, str]]
|
|
42
|
-
| collections.abc.Iterator[tuple[str, str]]
|
|
43
|
+
| collections.abc.Iterator[tuple[str, str]],
|
|
43
44
|
) -> None:
|
|
44
45
|
"""Call :meth:`print_formatted_text` with :class:`FormattedText`."""
|
|
45
46
|
print_formatted_text(FormattedText(fstrs))
|
|
@@ -47,7 +48,7 @@ def print_ftext(
|
|
|
47
48
|
|
|
48
49
|
def print_flist(
|
|
49
50
|
fstrs: collections.abc.Iterable[tuple[str, str]]
|
|
50
|
-
| collections.abc.Iterator[tuple[str, str]]
|
|
51
|
+
| collections.abc.Iterator[tuple[str, str]],
|
|
51
52
|
) -> None:
|
|
52
53
|
"""Print with :meth:`print_ftext` but interperse spaces."""
|
|
53
54
|
cols = os.get_terminal_size().columns
|
|
@@ -70,7 +71,7 @@ def print_row(
|
|
|
70
71
|
ftext: collections.abc.Iterable[tuple[str, str]]
|
|
71
72
|
| collections.abc.Iterator[tuple[str, str]]
|
|
72
73
|
| tuple[str, str]
|
|
73
|
-
| str
|
|
74
|
+
| str,
|
|
74
75
|
) -> None:
|
|
75
76
|
"""Print one line with hyphens when needed."""
|
|
76
77
|
if isinstance(ftext, str):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Validator for CLI."""
|
|
2
|
+
|
|
2
3
|
from prompt_toolkit.document import Document
|
|
3
4
|
from prompt_toolkit.validation import ValidationError, Validator
|
|
4
5
|
|
|
@@ -22,7 +23,7 @@ class CliValidator(Validator):
|
|
|
22
23
|
|
|
23
24
|
# Parameters
|
|
24
25
|
if CliFlags.COMPLETE_CALL in items.flags:
|
|
25
|
-
if items.method in
|
|
26
|
+
if items.method in {"ls", "dir"} and not self.config.raw:
|
|
26
27
|
return
|
|
27
28
|
if builtin.get_builtin(items.method[1:]):
|
|
28
29
|
# TODO we can add validation to the builtins as well
|
|
@@ -30,7 +31,7 @@ class CliValidator(Validator):
|
|
|
30
31
|
# Any other command should have CPON as argument and thus validate
|
|
31
32
|
# it as such.
|
|
32
33
|
try:
|
|
33
|
-
items.param
|
|
34
|
+
_ = items.param
|
|
34
35
|
except (ValueError, EOFError) as exc:
|
|
35
36
|
raise ValidationError(message=f"Invalid CPON: {exc}") from exc
|
|
36
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: shvcli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Silicon Heaven CLI access application
|
|
5
5
|
Author: Elektroline a.s.
|
|
6
6
|
License: MIT License
|
|
@@ -25,7 +25,7 @@ License: MIT License
|
|
|
25
25
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
26
|
SOFTWARE.
|
|
27
27
|
|
|
28
|
-
Project-URL: repository, https://gitlab.com/
|
|
28
|
+
Project-URL: repository, https://gitlab.com/silicon-heaven/shvcli
|
|
29
29
|
Classifier: Development Status :: 4 - Beta
|
|
30
30
|
Classifier: License :: OSI Approved :: MIT License
|
|
31
31
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -38,27 +38,22 @@ 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.0
|
|
42
42
|
Requires-Dist: prompt_toolkit
|
|
43
|
+
Requires-Dist: pygments
|
|
43
44
|
Requires-Dist: pyxdg
|
|
44
|
-
Provides-Extra: lint
|
|
45
|
-
Requires-Dist: black; extra == "lint"
|
|
46
|
-
Requires-Dist: isort; extra == "lint"
|
|
47
|
-
Requires-Dist: mypy; extra == "lint"
|
|
48
|
-
Requires-Dist: pydocstyle; extra == "lint"
|
|
49
|
-
Requires-Dist: pylint; extra == "lint"
|
|
50
45
|
|
|
51
46
|
=====================================
|
|
52
47
|
Silicon Heaven CLI access application
|
|
53
48
|
=====================================
|
|
54
|
-
.. image:: https://gitlab.com/
|
|
49
|
+
.. image:: https://gitlab.com/silicon-heaven/shvcli/-/raw/master/logo.svg
|
|
55
50
|
:align: right
|
|
56
51
|
:height: 128px
|
|
57
52
|
|
|
58
53
|
This provides an easy to use CLI interfase to access the SHV network.
|
|
59
54
|
|
|
60
|
-
* `📃 Sources <https://gitlab.com/
|
|
61
|
-
* `⁉️ Issue tracker <https://gitlab.com/
|
|
55
|
+
* `📃 Sources <https://gitlab.com/silicon-heaven/shvcli>`__
|
|
56
|
+
* `⁉️ Issue tracker <https://gitlab.com/silicon-heaven/shvcli/-/issues>`__
|
|
62
57
|
* `📕 Silicon Heaven protocol documentation <https://silicon-heaven.github.io/shv-doc/>`__
|
|
63
58
|
|
|
64
59
|
|
shvcli-0.3.0/pyproject.toml
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "shvcli"
|
|
3
|
-
description = "Silicon Heaven CLI access application"
|
|
4
|
-
readme = "README.rst"
|
|
5
|
-
license = {file = "LICENSE"}
|
|
6
|
-
dynamic = ["version"]
|
|
7
|
-
authors = [
|
|
8
|
-
{ name="Elektroline a.s." },
|
|
9
|
-
]
|
|
10
|
-
classifiers = [
|
|
11
|
-
"Development Status :: 4 - Beta",
|
|
12
|
-
"License :: OSI Approved :: MIT License",
|
|
13
|
-
"Programming Language :: Python :: 3",
|
|
14
|
-
"Operating System :: OS Independent",
|
|
15
|
-
"Environment :: Console",
|
|
16
|
-
"Intended Audience :: End Users/Desktop",
|
|
17
|
-
"Topic :: Home Automation",
|
|
18
|
-
"Topic :: System :: Shells",
|
|
19
|
-
"Topic :: Terminals",
|
|
20
|
-
]
|
|
21
|
-
requires-python = ">=3.11"
|
|
22
|
-
dependencies = [
|
|
23
|
-
"pyshv >= 0.5.0",
|
|
24
|
-
"prompt_toolkit",
|
|
25
|
-
"pyxdg",
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
[project.urls]
|
|
29
|
-
repository = "https://gitlab.com/elektroline-predator/shvcli"
|
|
30
|
-
|
|
31
|
-
[project.optional-dependencies]
|
|
32
|
-
lint = [
|
|
33
|
-
"black",
|
|
34
|
-
"isort",
|
|
35
|
-
"mypy",
|
|
36
|
-
"pydocstyle",
|
|
37
|
-
"pylint",
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
[project.scripts]
|
|
41
|
-
shvcli = "shvcli.__main__:main"
|
|
42
|
-
|
|
43
|
-
[build-system]
|
|
44
|
-
requires = ["setuptools"]
|
|
45
|
-
build-backend = "setuptools.build_meta"
|
|
46
|
-
|
|
47
|
-
[tool.setuptools.dynamic]
|
|
48
|
-
version = {file = "shvcli/version"}
|
|
49
|
-
[tool.setuptools.package-data]
|
|
50
|
-
"*" = ["version", "py.typed"]
|
|
51
|
-
|
|
52
|
-
[tool.coverage.run]
|
|
53
|
-
branch = true
|
|
54
|
-
[tool.coverage.report]
|
|
55
|
-
exclude_lines = [
|
|
56
|
-
"pragma: no cover",
|
|
57
|
-
"if typing.TYPE_CHECKING:"
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
[tool.isort]
|
|
61
|
-
profile = 'black'
|
|
62
|
-
|
|
63
|
-
[tool.pylint.main]
|
|
64
|
-
recursive = "y"
|
|
65
|
-
fail-under = 9.5
|
|
66
|
-
[tool.pylint.format]
|
|
67
|
-
disable = [
|
|
68
|
-
# Exception can be safely caught because there is BaseException for unsafe
|
|
69
|
-
# ones (such as SystemExit) and there are reasons to catch all exceptions.
|
|
70
|
-
"W0718",
|
|
71
|
-
# Prevent usage of module name in documentation (seems unreasonable)
|
|
72
|
-
"C0104",
|
|
73
|
-
# We have pydocstyle for the following ones
|
|
74
|
-
"C0103", "C0114", "C0115", "C0116",
|
|
75
|
-
]
|
|
76
|
-
# https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html
|
|
77
|
-
max-line-length = 88
|
|
78
|
-
|
|
79
|
-
[tool.mypy]
|
|
80
|
-
python_version = "3.11"
|
|
81
|
-
ignore_missing_imports = true
|
|
82
|
-
disallow_untyped_defs = true
|
|
83
|
-
warn_redundant_casts = true
|
|
84
|
-
warn_unused_ignores = true
|
|
85
|
-
warn_return_any = true
|
|
86
|
-
warn_unreachable = true
|
|
87
|
-
|
|
88
|
-
[tool.pydocstyle]
|
|
89
|
-
convention = "pep257"
|
shvcli-0.3.0/shvcli/__init__.py
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|