rats-apps 0.5.1.dev20250128192049__py3-none-any.whl → 0.6.0.dev20250207152453__py3-none-any.whl
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.
- rats/apps/_app_containers.py +3 -2
- rats/cli/__init__.py +11 -4
- rats/cli/_annotations.py +48 -1
- rats/cli/_app.py +22 -3
- rats/cli/_container.py +40 -2
- rats/cli/_plugin.py +43 -25
- {rats_apps-0.5.1.dev20250128192049.dist-info → rats_apps-0.6.0.dev20250207152453.dist-info}/METADATA +1 -1
- {rats_apps-0.5.1.dev20250128192049.dist-info → rats_apps-0.6.0.dev20250207152453.dist-info}/RECORD +9 -9
- {rats_apps-0.5.1.dev20250128192049.dist-info → rats_apps-0.6.0.dev20250207152453.dist-info}/WHEEL +0 -0
rats/apps/_app_containers.py
CHANGED
@@ -63,10 +63,11 @@ be used easily in functions that need to defer the construction of an applicatio
|
|
63
63
|
|
64
64
|
class PluginMixin:
|
65
65
|
"""
|
66
|
-
Mix into your [Container][] classes to add our default constructor.
|
66
|
+
Mix into your [rats.apps.Container][] classes to add our default constructor.
|
67
67
|
|
68
68
|
This mixin adds a common constructor to a `Container` in order to quickly create types that
|
69
|
-
are compatible with functions asking for
|
69
|
+
are compatible with functions asking for [rats.apps.AppPlugin][] and
|
70
|
+
[rats.apps.ContainerPlugin][] arguments.
|
70
71
|
|
71
72
|
!!! warning
|
72
73
|
Avoid using mixins as an input type to your functions, because we don't want to restrict
|
rats/cli/__init__.py
CHANGED
@@ -1,16 +1,23 @@
|
|
1
|
-
"""
|
1
|
+
"""Use `rats.cli` to streamline the creation of CLI commands written with Click."""
|
2
2
|
|
3
|
-
from ._annotations import
|
3
|
+
from ._annotations import (
|
4
|
+
AUTO_COMMAND,
|
5
|
+
CommandId,
|
6
|
+
command,
|
7
|
+
get_class_commands,
|
8
|
+
get_class_groups,
|
9
|
+
group,
|
10
|
+
)
|
4
11
|
from ._app import ClickApp
|
5
12
|
from ._container import CompositeContainer, Container
|
6
|
-
from ._plugin import
|
13
|
+
from ._plugin import attach, create_group
|
7
14
|
|
8
15
|
__all__ = [
|
16
|
+
"AUTO_COMMAND",
|
9
17
|
"ClickApp",
|
10
18
|
"CommandId",
|
11
19
|
"CompositeContainer",
|
12
20
|
"Container",
|
13
|
-
"PluginServices",
|
14
21
|
"attach",
|
15
22
|
"command",
|
16
23
|
"create_group",
|
rats/cli/_annotations.py
CHANGED
@@ -8,14 +8,47 @@ from rats import apps
|
|
8
8
|
|
9
9
|
|
10
10
|
class CommandId(NamedTuple):
|
11
|
+
"""
|
12
|
+
A small wrapper around the string name of a [click.Command][].
|
13
|
+
|
14
|
+
Used when wanting to specify a custom name for a command within a [rats.cli.Container][] class.
|
15
|
+
See [rats.cli.command][] for more details.
|
16
|
+
"""
|
17
|
+
|
11
18
|
name: str
|
12
19
|
|
13
20
|
|
14
21
|
AUTO_COMMAND = CommandId("__auto__")
|
22
|
+
"""
|
23
|
+
Causes command names to be generated from the method names in [rats.cli.Container][]'s.
|
24
|
+
|
25
|
+
This is the default option for [rats.cli.command][] and [rats.cli.group][] and typically does not
|
26
|
+
need to be used directly.
|
27
|
+
"""
|
15
28
|
T = TypeVar("T", bound=Callable[[Any], Any])
|
16
29
|
|
17
30
|
|
18
31
|
def command(command_id: CommandId = AUTO_COMMAND) -> Callable[..., apps.Executable]:
|
32
|
+
"""
|
33
|
+
Mark a method in [rats.cli.Container][] instances as a [click.Command][].
|
34
|
+
|
35
|
+
By default, the name of the method is used to generate a dash-separated command name. You can
|
36
|
+
provide a [rats.cli.CommandId][] argument to specify a custom name, for example, if the desired
|
37
|
+
command contains characters that are invalid in method names, like periods.
|
38
|
+
|
39
|
+
```python
|
40
|
+
from rats import cli
|
41
|
+
|
42
|
+
class Example(cli.Container):
|
43
|
+
@cli.command(cli.CommandId("some.example-command")
|
44
|
+
def _any_method_name(self) -> None:
|
45
|
+
print("this cli command is called some.example-command")
|
46
|
+
```
|
47
|
+
|
48
|
+
Args:
|
49
|
+
command_id: An optional [rats.cli.CommandId][] to specify the command name.
|
50
|
+
"""
|
51
|
+
|
19
52
|
def decorator(fn: T) -> T:
|
20
53
|
if command_id == AUTO_COMMAND:
|
21
54
|
cmd_name = fn.__name__.replace("_", "-").strip("-")
|
@@ -26,17 +59,31 @@ def command(command_id: CommandId = AUTO_COMMAND) -> Callable[..., apps.Executab
|
|
26
59
|
|
27
60
|
|
28
61
|
def group(command_id: CommandId = AUTO_COMMAND) -> Callable[..., apps.Executable]:
|
62
|
+
"""
|
63
|
+
Mark a method in [rats.cli.Container][] instances as a [click.Group][].
|
64
|
+
|
65
|
+
!!! warning
|
66
|
+
It's unclear if this API is useful as-is and will most likely change soon.
|
67
|
+
|
68
|
+
See [rats.cli.command][] for more details.
|
69
|
+
|
70
|
+
Args:
|
71
|
+
command_id: An optional [rats.cli.CommandId][] to specify the group name.
|
72
|
+
"""
|
73
|
+
|
29
74
|
def decorator(fn: T) -> T:
|
30
75
|
if command_id == AUTO_COMMAND:
|
31
76
|
return anns.annotation("command-groups", CommandId(fn.__name__.replace("_", "-")))(fn)
|
32
|
-
return anns.annotation("
|
77
|
+
return anns.annotation("command-groups", command_id)(fn)
|
33
78
|
|
34
79
|
return decorator # type: ignore[reportReturnType]
|
35
80
|
|
36
81
|
|
37
82
|
def get_class_commands(cls: type) -> anns.AnnotationsContainer:
|
83
|
+
"""Extracts class methods decorated with [rats.cli.command][]."""
|
38
84
|
return anns.get_class_annotations(cls).with_namespace("commands")
|
39
85
|
|
40
86
|
|
41
87
|
def get_class_groups(cls: type) -> anns.AnnotationsContainer:
|
88
|
+
"""Extracts class methods decorated with [rats.cli.group][]."""
|
42
89
|
return anns.get_class_annotations(cls).with_namespace("command-groups")
|
rats/cli/_app.py
CHANGED
@@ -9,7 +9,13 @@ from ._container import Container
|
|
9
9
|
|
10
10
|
@final
|
11
11
|
class ClickApp(apps.Executable):
|
12
|
-
"""
|
12
|
+
"""
|
13
|
+
A wrapper to configure and run a [click.Group][] as a [rats.apps.Executable][].
|
14
|
+
|
15
|
+
The [rats.cli.Container][] is attached to the provided [click.Group][] when the
|
16
|
+
[rats.cli.ClickApp.execute][] is called, allowing some simple forms of lazy-loading to keep
|
17
|
+
commands snappy.
|
18
|
+
"""
|
13
19
|
|
14
20
|
_group: click.Group
|
15
21
|
_commands: Container
|
@@ -19,11 +25,24 @@ class ClickApp(apps.Executable):
|
|
19
25
|
group: click.Group,
|
20
26
|
commands: Container,
|
21
27
|
) -> None:
|
22
|
-
"""
|
28
|
+
"""
|
29
|
+
Provide the [rats.cli.Container][] instance that makes up the wanted cli commands.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
group: group that will be executed by this application.
|
33
|
+
commands: container that will be attached before executing the above group.
|
34
|
+
"""
|
23
35
|
self._group = group
|
24
36
|
self._commands = commands
|
25
37
|
|
26
38
|
def execute(self) -> None:
|
27
|
-
"""
|
39
|
+
"""
|
40
|
+
Attach any commands provided by the [rats.cli.Container][] and then execute the group.
|
41
|
+
|
42
|
+
!!! note
|
43
|
+
Currently, this method runs [click.BaseCommand.main][] without arguments, causing Click
|
44
|
+
to pull the command arguments from [sys.argv][]. If you are needing to change this,
|
45
|
+
skip using this class and implement the custom logic yourself.
|
46
|
+
"""
|
28
47
|
self._commands.attach(self._group)
|
29
48
|
self._group.main()
|
rats/cli/_container.py
CHANGED
@@ -11,10 +11,34 @@ logger = logging.getLogger(__name__)
|
|
11
11
|
|
12
12
|
|
13
13
|
class Container(Protocol):
|
14
|
-
"""
|
14
|
+
"""
|
15
|
+
A container that can attach click commands to a click group.
|
16
|
+
|
17
|
+
!!! example
|
18
|
+
The default protocol implementation detects any methods in the class decorated with the
|
19
|
+
[rats.cli.command][] annotation. Apart from marking the method as a cli command, you can
|
20
|
+
add [click.argument][] and [click.option][] decorators as defined by the click docs.
|
21
|
+
|
22
|
+
```python
|
23
|
+
from rats import cli
|
24
|
+
|
25
|
+
|
26
|
+
class ExampleCli(cli.Container):
|
27
|
+
@cli.command()
|
28
|
+
@click.argument("something")
|
29
|
+
def my_cmd(self, something: str) -> None:
|
30
|
+
print("this command is automatically detected and attached as my-cmd")
|
31
|
+
print("add a docstring to the method to be used as the --help text")
|
32
|
+
```
|
33
|
+
"""
|
15
34
|
|
16
35
|
def attach(self, group: click.Group) -> None:
|
17
|
-
"""
|
36
|
+
"""
|
37
|
+
Attach this container's provided cli commands to the provided [click.Group][].
|
38
|
+
|
39
|
+
Args:
|
40
|
+
group: The [click.Group][] we are attaching our commands to.
|
41
|
+
"""
|
18
42
|
|
19
43
|
def cb(_method: Callable[..., None], *args: Any, **kwargs: Any) -> None:
|
20
44
|
"""
|
@@ -50,11 +74,25 @@ class Container(Protocol):
|
|
50
74
|
|
51
75
|
@final
|
52
76
|
class CompositeContainer(Container):
|
77
|
+
"""Take any number of [rats.cli.Container][] instances and expose them as if they were one."""
|
78
|
+
|
53
79
|
_containers: tuple[Container, ...]
|
54
80
|
|
55
81
|
def __init__(self, *containers: Container) -> None:
|
82
|
+
"""
|
83
|
+
Zero or more instances can be provided.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
*containers: Any number of container instances wanting to be merged.
|
87
|
+
"""
|
56
88
|
self._containers = containers
|
57
89
|
|
58
90
|
def attach(self, group: click.Group) -> None:
|
91
|
+
"""
|
92
|
+
Attach the cli commands from all [rats.cli.Container][] instances to the [click.Group][].
|
93
|
+
|
94
|
+
Args:
|
95
|
+
group: The [click.Group][] we are attaching our commands to.
|
96
|
+
"""
|
59
97
|
for container in self._containers:
|
60
98
|
container.attach(group)
|
rats/cli/_plugin.py
CHANGED
@@ -1,13 +1,31 @@
|
|
1
|
-
from typing import cast
|
2
|
-
|
3
1
|
import click
|
4
2
|
|
5
|
-
from rats import apps
|
6
|
-
|
7
3
|
from ._container import Container
|
8
4
|
|
9
5
|
|
10
6
|
def create_group(group: click.Group, container: Container) -> click.Group:
|
7
|
+
"""
|
8
|
+
A simple shortcut to create a [click.Group][] with commands provided by `rats.cli.Container`'s.
|
9
|
+
|
10
|
+
```python
|
11
|
+
import click
|
12
|
+
from rats import cli
|
13
|
+
|
14
|
+
|
15
|
+
class Example(cli.Container):
|
16
|
+
@cli.command()
|
17
|
+
def my_command(self) -> None:
|
18
|
+
print("hello, world. run me with `my-cli my-command`.")
|
19
|
+
|
20
|
+
|
21
|
+
if __name__ == "__main__":
|
22
|
+
cli.create_group(click.Group("my-cli"), Example()).main()
|
23
|
+
```
|
24
|
+
|
25
|
+
Args:
|
26
|
+
group: The [click.Group][] commands will be added to.
|
27
|
+
container: The [rats.cli.Container][] instance to attach commands from
|
28
|
+
"""
|
11
29
|
container.attach(group)
|
12
30
|
return group
|
13
31
|
|
@@ -17,27 +35,27 @@ def attach(
|
|
17
35
|
command: click.Command | click.Group,
|
18
36
|
*commands: click.Command | click.Group,
|
19
37
|
) -> None:
|
38
|
+
"""
|
39
|
+
Convenience function to attach multiple [click.Command]'s to a [click.Group].
|
40
|
+
|
41
|
+
```python
|
42
|
+
import click
|
43
|
+
from rats import cli
|
44
|
+
|
45
|
+
cli.attach(
|
46
|
+
click.Group("my-cli"),
|
47
|
+
click.Command("command-1"),
|
48
|
+
click.Command("command-2"),
|
49
|
+
)
|
50
|
+
```
|
51
|
+
|
52
|
+
Args:
|
53
|
+
group: The [click.Group] we want to build.
|
54
|
+
command: A [click.Command] to attach to the provided group.
|
55
|
+
*commands: Any number of additional [click.Command]'s to attach.
|
56
|
+
|
57
|
+
Returns: The input [click.Group][] with all of the commands attached.
|
58
|
+
"""
|
20
59
|
group.add_command(command)
|
21
60
|
for c in commands:
|
22
61
|
group.add_command(c)
|
23
|
-
|
24
|
-
|
25
|
-
@apps.autoscope
|
26
|
-
class _PluginEvents:
|
27
|
-
pass
|
28
|
-
|
29
|
-
|
30
|
-
@apps.autoscope
|
31
|
-
class PluginServices:
|
32
|
-
EVENTS = _PluginEvents
|
33
|
-
|
34
|
-
@staticmethod
|
35
|
-
def sub_command(
|
36
|
-
parent: apps.ServiceId[apps.Executable],
|
37
|
-
name: str,
|
38
|
-
) -> apps.ServiceId[apps.Executable]:
|
39
|
-
return apps.ServiceId(f"{parent.name}[{name}]")
|
40
|
-
|
41
|
-
@staticmethod
|
42
|
-
def click_command(cmd_id: apps.ServiceId[apps.Executable]) -> apps.ServiceId[click.Group]:
|
43
|
-
return cast(apps.ServiceId[click.Group], cmd_id)
|
{rats_apps-0.5.1.dev20250128192049.dist-info → rats_apps-0.6.0.dev20250207152453.dist-info}/RECORD
RENAMED
@@ -4,7 +4,7 @@ rats/annotations/_functions.py,sha256=UkHh3zdBivluE7dBeGQ17zoIfGdyIokMAkFmpWaIlD
|
|
4
4
|
rats/annotations/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
rats/apps/__init__.py,sha256=uTPk2ipOy7MgLoV0s9l22U1Qyo8aL2yKVwDukOY9WGw,1934
|
6
6
|
rats/apps/_annotations.py,sha256=6M_M7K8haNVda0Tx02EpFf3s9EjnWYacNMjTIkNEdRU,4617
|
7
|
-
rats/apps/_app_containers.py,sha256=
|
7
|
+
rats/apps/_app_containers.py,sha256=ygZL0NM-fSRSemFddBsVMwcmSQw07sbb7bIqWTr-_yU,6147
|
8
8
|
rats/apps/_composite_container.py,sha256=s_of6NyyrjFVYWGVehyEHe9WJIPRCnbB-tyWyNF8zyc,585
|
9
9
|
rats/apps/_container.py,sha256=sYISCG-qVzl-bsBwX_CAWEP9gtXCnG-Jls3l8IKb4DQ,7208
|
10
10
|
rats/apps/_executables.py,sha256=hXExNmAnuPU1KJXihNw1jEDAQpMlQ9E9_aPV8tpGbOY,1347
|
@@ -16,12 +16,12 @@ rats/apps/_runtimes.py,sha256=l5Z26jZVHk4CvT8VyKUK5tpcCAKt2N1DWipd4gX_Bls,2060
|
|
16
16
|
rats/apps/_scoping.py,sha256=6C2-ID22cCPR9Cbexf3CvCF3o9F_7ieURbwqkf6DI68,1360
|
17
17
|
rats/apps/_static_container.py,sha256=KH4AwRMX5QPIwYrD9W_HayIpQIrbVn7clEMx44LFAGc,2113
|
18
18
|
rats/apps/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
-
rats/cli/__init__.py,sha256=
|
19
|
+
rats/cli/__init__.py,sha256=0VDmmNP4JOW-hNjufYwO-e38kLpquWb_fo95geuNv5w,564
|
20
20
|
rats/cli/__main__.py,sha256=WeldAKjA3Kmz9ZRnZVd3G8Ud66Y_gSRDLIPNE1JyhV0,1418
|
21
|
-
rats/cli/_annotations.py,sha256=
|
22
|
-
rats/cli/_app.py,sha256=
|
23
|
-
rats/cli/_container.py,sha256=
|
24
|
-
rats/cli/_plugin.py,sha256=
|
21
|
+
rats/cli/_annotations.py,sha256=TCjs2b7o6kUSseFwrHm63o9QurwGDO7YsK56ZNMQPGg,3014
|
22
|
+
rats/cli/_app.py,sha256=4z2jiRHafCAN7S-cbMhfQJhLGwl6ljFS3r6P1COgR00,1454
|
23
|
+
rats/cli/_container.py,sha256=FIQBqi9PTNpE2P52qkfGET51BczdD-JvcWXLTjCNPDI,3512
|
24
|
+
rats/cli/_plugin.py,sha256=BNmgWVquQUEqJAYsed_l8vLnlLP7u3XC1TDyEFI1AiU,1552
|
25
25
|
rats/cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
26
|
rats/logs/__init__.py,sha256=sizLa6JbqekhJGKDyRZUUQvTuqiJx6yxwA5jT-tDQck,152
|
27
27
|
rats/logs/_app.py,sha256=TF6rfQXQfaHd3AFW2anAoAGaxzU31kqJMoKkjuBtbF0,1617
|
@@ -35,6 +35,6 @@ rats_e2e/apps/inputs/_app.py,sha256=FiaLgOZc-d1ryKSwKnL5XBNGcOP1bHbxxeMJqoU_RJg,
|
|
35
35
|
rats_e2e/apps/minimal/__init__.py,sha256=bUR6Oexx6Jsouxor0cL9emXoVha4cm3WqyhU1pgchsI,521
|
36
36
|
rats_e2e/apps/minimal/__main__.py,sha256=Mf-a2iQKTTgh9hMd6AeuzmU9araMIyf1AtdWkh_L07E,117
|
37
37
|
rats_e2e/apps/minimal/_app.py,sha256=CQ09LVTNRarz7Pb1wiSuNHrZ_2KGcgH8nUqy4BjxMUY,849
|
38
|
-
rats_apps-0.
|
39
|
-
rats_apps-0.
|
40
|
-
rats_apps-0.
|
38
|
+
rats_apps-0.6.0.dev20250207152453.dist-info/METADATA,sha256=NSS2NJbeZ1O-du3MXAgRdb68VYaRZ-O5mdHdAn9vqt0,779
|
39
|
+
rats_apps-0.6.0.dev20250207152453.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
40
|
+
rats_apps-0.6.0.dev20250207152453.dist-info/RECORD,,
|
{rats_apps-0.5.1.dev20250128192049.dist-info → rats_apps-0.6.0.dev20250207152453.dist-info}/WHEEL
RENAMED
File without changes
|