rats-apps 0.4.0.dev20241212213929__py3-none-any.whl → 0.5.0.dev20250102200830__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/annotations/__init__.py +4 -4
- rats/apps/__init__.py +33 -22
- rats/apps/_app_containers.py +199 -0
- rats/apps/_container.py +0 -4
- rats/apps/_executables.py +15 -1
- rats/apps/_mains.py +42 -0
- rats/apps/_runtimes.py +20 -8
- rats/apps/_static_container.py +44 -7
- rats/cli/__init__.py +8 -9
- rats/cli/__main__.py +7 -15
- rats/cli/_plugin.py +1 -9
- rats/logs/__init__.py +2 -3
- rats/logs/_app.py +45 -0
- {rats_apps-0.4.0.dev20241212213929.dist-info → rats_apps-0.5.0.dev20250102200830.dist-info}/METADATA +1 -1
- rats_apps-0.5.0.dev20250102200830.dist-info/RECORD +40 -0
- rats_e2e/apps/__init__.py +7 -0
- rats_e2e/apps/__main__.py +17 -0
- rats_e2e/apps/_example_cli.py +82 -0
- rats_e2e/apps/inputs/__init__.py +31 -0
- rats_e2e/apps/inputs/__main__.py +6 -0
- rats_e2e/apps/inputs/_app.py +46 -0
- rats_e2e/apps/minimal/__init__.py +26 -0
- rats_e2e/apps/minimal/__main__.py +6 -0
- rats_e2e/apps/minimal/_app.py +28 -0
- rats/apps/__main__.py +0 -79
- rats/apps/_plugins.py +0 -23
- rats/apps/_simple_apps.py +0 -163
- rats/logs/_plugin.py +0 -64
- rats_apps-0.4.0.dev20241212213929.dist-info/RECORD +0 -33
- rats_apps-0.4.0.dev20241212213929.dist-info/entry_points.txt +0 -4
- {rats_apps-0.4.0.dev20241212213929.dist-info → rats_apps-0.5.0.dev20250102200830.dist-info}/WHEEL +0 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
rats/annotations/__init__.py,sha256=YmydaopUSlpoX6MvQ-3p18sgEC_DB_y683wVICD-jnU,498
|
2
|
+
rats/annotations/__main__.py,sha256=vlzQOM9y82P0OL5tYcmSM_4jTg0s8jayAcvEoi9cBvI,1065
|
3
|
+
rats/annotations/_functions.py,sha256=UkHh3zdBivluE7dBeGQ17zoIfGdyIokMAkFmpWaIlDc,4284
|
4
|
+
rats/annotations/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
rats/apps/__init__.py,sha256=ZUFEuocGQTyWKOZlkYH3Q09NujfpAN-whTnFFg5-Rf4,1916
|
6
|
+
rats/apps/_annotations.py,sha256=6M_M7K8haNVda0Tx02EpFf3s9EjnWYacNMjTIkNEdRU,4617
|
7
|
+
rats/apps/_app_containers.py,sha256=Ym2NrRljmZVE7EXK2G8KgDHAGreYsuFlvu1QYTkhOsE,6109
|
8
|
+
rats/apps/_composite_container.py,sha256=s_of6NyyrjFVYWGVehyEHe9WJIPRCnbB-tyWyNF8zyc,585
|
9
|
+
rats/apps/_container.py,sha256=sYISCG-qVzl-bsBwX_CAWEP9gtXCnG-Jls3l8IKb4DQ,7208
|
10
|
+
rats/apps/_executables.py,sha256=hXExNmAnuPU1KJXihNw1jEDAQpMlQ9E9_aPV8tpGbOY,1347
|
11
|
+
rats/apps/_ids.py,sha256=T8Onrj79t8NPfBMQBk0xI6fIWDKF0m2JfFNrdtXAbWg,353
|
12
|
+
rats/apps/_mains.py,sha256=2Q97mNk1cBzYROc_pJcm57EEeHmwRbXOWpfYXH37qcA,995
|
13
|
+
rats/apps/_namespaces.py,sha256=THUV_Xj5PtweC23Ob-zsSpk8exC4fT-qRwjpQ6IDm0U,188
|
14
|
+
rats/apps/_plugin_container.py,sha256=wmaBgxmvKo82ue9CrKHRXafgik5wIXh8XkEYMfhcTjs,1530
|
15
|
+
rats/apps/_runtimes.py,sha256=l5Z26jZVHk4CvT8VyKUK5tpcCAKt2N1DWipd4gX_Bls,2060
|
16
|
+
rats/apps/_scoping.py,sha256=6C2-ID22cCPR9Cbexf3CvCF3o9F_7ieURbwqkf6DI68,1360
|
17
|
+
rats/apps/_static_container.py,sha256=KH4AwRMX5QPIwYrD9W_HayIpQIrbVn7clEMx44LFAGc,2113
|
18
|
+
rats/apps/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
+
rats/cli/__init__.py,sha256=PFwnpWtiAkHyV_wyQRNslKHKAOI659HpU5KBfswq8RQ,540
|
20
|
+
rats/cli/__main__.py,sha256=WeldAKjA3Kmz9ZRnZVd3G8Ud66Y_gSRDLIPNE1JyhV0,1418
|
21
|
+
rats/cli/_annotations.py,sha256=5voM1pNm7iybZpgQTSVpaq3rMIMz33jr4eUyoEmxWJ4,1359
|
22
|
+
rats/cli/_app.py,sha256=EmLIJe1LTLob5u5gP8gpgxk3kFlALTvrpYlwxlILu0k,620
|
23
|
+
rats/cli/_container.py,sha256=q3L6On6RS3PfUFZ67t6eUvKUQIK4Qxomz5ob86aNcR4,2143
|
24
|
+
rats/cli/_plugin.py,sha256=IknKt0yLNpk2ZYH2yRiADC74jO5DowAGmoWOEvR6l4g,928
|
25
|
+
rats/cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
|
+
rats/logs/__init__.py,sha256=sizLa6JbqekhJGKDyRZUUQvTuqiJx6yxwA5jT-tDQck,152
|
27
|
+
rats/logs/_app.py,sha256=TF6rfQXQfaHd3AFW2anAoAGaxzU31kqJMoKkjuBtbF0,1617
|
28
|
+
rats/logs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
|
+
rats_e2e/apps/__init__.py,sha256=c2QG818ACyS9MBXs_Evg5yYT6k-KPMQfIqF7Z77Cau0,142
|
30
|
+
rats_e2e/apps/__main__.py,sha256=Ha_FTjgnX695huoXTBulqLUjKGG0aEwqXZx8tRDYKdY,351
|
31
|
+
rats_e2e/apps/_example_cli.py,sha256=76bOZcYCNsRkNaRU81Wc1pIkjNaR0k7Bg1L0AiN79_A,2840
|
32
|
+
rats_e2e/apps/inputs/__init__.py,sha256=A9OActelrdQR-YYxUAGzogUFiOt3sDayGtQ3_6orVzc,785
|
33
|
+
rats_e2e/apps/inputs/__main__.py,sha256=Mf-a2iQKTTgh9hMd6AeuzmU9araMIyf1AtdWkh_L07E,117
|
34
|
+
rats_e2e/apps/inputs/_app.py,sha256=FiaLgOZc-d1ryKSwKnL5XBNGcOP1bHbxxeMJqoU_RJg,1446
|
35
|
+
rats_e2e/apps/minimal/__init__.py,sha256=bUR6Oexx6Jsouxor0cL9emXoVha4cm3WqyhU1pgchsI,521
|
36
|
+
rats_e2e/apps/minimal/__main__.py,sha256=Mf-a2iQKTTgh9hMd6AeuzmU9araMIyf1AtdWkh_L07E,117
|
37
|
+
rats_e2e/apps/minimal/_app.py,sha256=CQ09LVTNRarz7Pb1wiSuNHrZ_2KGcgH8nUqy4BjxMUY,849
|
38
|
+
rats_apps-0.5.0.dev20250102200830.dist-info/METADATA,sha256=PoWUQ6BgiO0mAjT0itv2W9nGAQ2AGKe6idcQl74P8Rw,825
|
39
|
+
rats_apps-0.5.0.dev20250102200830.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
40
|
+
rats_apps-0.5.0.dev20250102200830.dist-info/RECORD,,
|
@@ -0,0 +1,17 @@
|
|
1
|
+
"""
|
2
|
+
A set of cli commands that serve as examples of how to use the `rats.apps` package.
|
3
|
+
|
4
|
+
Run this module with coverage.py: `coverage run --branch -m rats_e2e.runtime`.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
|
9
|
+
from rats_e2e import apps as e2e
|
10
|
+
|
11
|
+
from rats import apps
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
if __name__ == "__main__":
|
17
|
+
apps.run_plugin(e2e.ExampleCliApp)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
from random import randint
|
2
|
+
from textwrap import dedent
|
3
|
+
|
4
|
+
import click
|
5
|
+
import rats_e2e.apps.inputs as inputs
|
6
|
+
import rats_e2e.apps.minimal as minimal
|
7
|
+
|
8
|
+
from rats import apps, cli, logs
|
9
|
+
|
10
|
+
|
11
|
+
class ExampleContextContainer(apps.Container, apps.PluginMixin):
|
12
|
+
"""
|
13
|
+
Our test context container provides the input data to our applications.
|
14
|
+
|
15
|
+
We'll pass this container into the different example applications, so the developer of a given
|
16
|
+
application can access the services defined here as being part of their parent container
|
17
|
+
defined in `self._app`.
|
18
|
+
"""
|
19
|
+
|
20
|
+
@apps.service(inputs.AppServices.INPUT)
|
21
|
+
def _input(self) -> inputs.AppInput:
|
22
|
+
"""
|
23
|
+
A random value we want to be made available to our example `inputs.Application`.
|
24
|
+
|
25
|
+
Because the `apps.Container` service providers are cached, our `uuid` will be static for
|
26
|
+
each instance of this container
|
27
|
+
"""
|
28
|
+
return inputs.AppInput(randint(0, 5))
|
29
|
+
|
30
|
+
|
31
|
+
class ExampleCliApp(apps.AppContainer, cli.Container, apps.PluginMixin):
|
32
|
+
"""Test application that exposes a few example uses of `rats.apps`."""
|
33
|
+
|
34
|
+
def execute(self) -> None:
|
35
|
+
"""Register our cli commands and run them using click."""
|
36
|
+
apps.run(apps.AppBundle(app_plugin=logs.ConfigureApplication))
|
37
|
+
cli.create_group(click.Group("rats_e2e.apps"), self)()
|
38
|
+
|
39
|
+
@cli.command()
|
40
|
+
def _run_all(self) -> None:
|
41
|
+
"""Run examples using `apps.AppBundle()`."""
|
42
|
+
app_plugins = [
|
43
|
+
minimal.Application,
|
44
|
+
inputs.Application,
|
45
|
+
]
|
46
|
+
|
47
|
+
print("running the following applications using the `apps.AppBundle` class:")
|
48
|
+
for cls in app_plugins:
|
49
|
+
print(f" - {cls.__module__}:{cls.__name__}")
|
50
|
+
|
51
|
+
print("\nfirst running apps without any injected context")
|
52
|
+
print("=" * 20)
|
53
|
+
|
54
|
+
apps.run_plugin(*app_plugins)
|
55
|
+
|
56
|
+
print("=" * 20)
|
57
|
+
|
58
|
+
print(
|
59
|
+
dedent(f"""
|
60
|
+
running with external context container: {ExampleContextContainer}
|
61
|
+
when we specify the context using the `container_plugin` argument, each application
|
62
|
+
will receive a new instance of the context built by the `AppBundle` class.
|
63
|
+
""")
|
64
|
+
)
|
65
|
+
print("=" * 20)
|
66
|
+
|
67
|
+
for cls in app_plugins:
|
68
|
+
apps.run(apps.AppBundle(app_plugin=cls, container_plugin=ExampleContextContainer))
|
69
|
+
|
70
|
+
print("=" * 20)
|
71
|
+
print(
|
72
|
+
dedent(f"""
|
73
|
+
running with shared external context container: {ExampleContextContainer}
|
74
|
+
when we specify the context using the `context` argument, each application can be
|
75
|
+
given the same instance.
|
76
|
+
""")
|
77
|
+
)
|
78
|
+
print("=" * 20)
|
79
|
+
|
80
|
+
ctx = ExampleContextContainer(apps.CompositeContainer())
|
81
|
+
for cls in app_plugins:
|
82
|
+
apps.run(apps.AppBundle(app_plugin=cls, context=ctx))
|
@@ -0,0 +1,31 @@
|
|
1
|
+
"""
|
2
|
+
Application using services expected to be provided externally.
|
3
|
+
|
4
|
+
Instead of using the `RATS_E2E_NUM_VALUES` environment variable, this application introduces a
|
5
|
+
small configuration object [rats_e2e.apps.inputs.AppInput][] that we can specify when running
|
6
|
+
things.
|
7
|
+
|
8
|
+
```python
|
9
|
+
from rats import apps
|
10
|
+
from rats_e2e.apps import inputs
|
11
|
+
|
12
|
+
|
13
|
+
class AppContext(apps.Container, apps.PluginMixin):
|
14
|
+
@apps.service(inputs.AppServices.INPUT)
|
15
|
+
def _input(self) -> inputs.AppInput:
|
16
|
+
return inputs.AppInput(randint(0, 5))
|
17
|
+
|
18
|
+
|
19
|
+
ctx = ExampleContextContainer(apps.AppContext())
|
20
|
+
apps.run(apps.AppBundle(app_plugin=inputs.Application, context=ctx))
|
21
|
+
```
|
22
|
+
"""
|
23
|
+
|
24
|
+
from ._app import AppInput, Application, AppServices, main
|
25
|
+
|
26
|
+
__all__ = [
|
27
|
+
"AppInput",
|
28
|
+
"AppServices",
|
29
|
+
"Application",
|
30
|
+
"main",
|
31
|
+
]
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import NamedTuple
|
3
|
+
from uuid import uuid4
|
4
|
+
|
5
|
+
from rats import apps
|
6
|
+
|
7
|
+
logger = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
|
10
|
+
class AppInput(NamedTuple):
|
11
|
+
"""A small data structure to provide the needed configuration for our application."""
|
12
|
+
|
13
|
+
num_rows: int
|
14
|
+
"""The number of values we want printed by the [rats_e2e.apps.inputs.Application][] class."""
|
15
|
+
|
16
|
+
|
17
|
+
@apps.autoscope
|
18
|
+
class AppServices:
|
19
|
+
INPUT = apps.ServiceId[AppInput]("input")
|
20
|
+
|
21
|
+
|
22
|
+
class Application(apps.AppContainer, apps.PluginMixin):
|
23
|
+
"""
|
24
|
+
Prints a handful of random values to stdout.
|
25
|
+
|
26
|
+
We can run this application with `apps.run_plugin(minimal.Application)` or directly in the
|
27
|
+
terminal with `python -m rats_e2e.apps.inputs`. However, the library api allows the addition
|
28
|
+
of a [rats.apps.Container][] with a service used as configuration.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def execute(self) -> None:
|
32
|
+
"""The main entry point to the application."""
|
33
|
+
app_input = self._app.get(AppServices.INPUT)
|
34
|
+
logger.info(f"running inputs application example with input: {app_input}")
|
35
|
+
for _x in range(app_input.num_rows):
|
36
|
+
print(uuid4())
|
37
|
+
|
38
|
+
@apps.fallback_service(AppServices.INPUT)
|
39
|
+
def _default_input(self) -> AppInput:
|
40
|
+
# we can throw an exception if we want the input to be required without defaults
|
41
|
+
logger.warning("default app input being used")
|
42
|
+
return AppInput(5)
|
43
|
+
|
44
|
+
|
45
|
+
def main() -> None:
|
46
|
+
apps.run_plugin(Application)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
A minimal executable application example.
|
3
|
+
|
4
|
+
You can run this example by using the [rats_e2e.apps.minimal.Application][] class within python,
|
5
|
+
or directly through a terminal.
|
6
|
+
|
7
|
+
=== ":material-language-python: python"
|
8
|
+
```python
|
9
|
+
from rats import apps
|
10
|
+
from rats_e2e.apps import minimal
|
11
|
+
|
12
|
+
apps.run_plugin(minimal.Application)
|
13
|
+
```
|
14
|
+
|
15
|
+
=== ":material-console: ~/code"
|
16
|
+
```bash
|
17
|
+
python -m rats_e2e.apps.minimal
|
18
|
+
```
|
19
|
+
"""
|
20
|
+
|
21
|
+
from ._app import Application, main
|
22
|
+
|
23
|
+
__all__ = [
|
24
|
+
"Application",
|
25
|
+
"main",
|
26
|
+
]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
from uuid import uuid4
|
4
|
+
|
5
|
+
from rats import apps
|
6
|
+
|
7
|
+
logger = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
|
10
|
+
class Application(apps.AppContainer, apps.PluginMixin):
|
11
|
+
"""
|
12
|
+
Prints a handful of random values to stdout.
|
13
|
+
|
14
|
+
We can run this application with `apps.run_plugin(minimal.Application)` or directly in the
|
15
|
+
terminal with `python -m rats_e2e.apps.minimal`. Use the `RATS_E2E_NUM_VALUES` environment
|
16
|
+
variable to alter the number of values to print.
|
17
|
+
"""
|
18
|
+
|
19
|
+
def execute(self) -> None:
|
20
|
+
"""The main entry point to the application."""
|
21
|
+
logger.info("running minimal application example")
|
22
|
+
for _x in range(int(os.environ.get("RATS_E2E_NUM_VALUES", 5))):
|
23
|
+
print(uuid4())
|
24
|
+
|
25
|
+
|
26
|
+
def main() -> None:
|
27
|
+
"""Entry point function to register this application as a script."""
|
28
|
+
apps.run_plugin(Application)
|
rats/apps/__main__.py
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
"""..."""
|
2
|
-
|
3
|
-
import json
|
4
|
-
import logging
|
5
|
-
import os
|
6
|
-
import uuid
|
7
|
-
from collections.abc import Iterator
|
8
|
-
|
9
|
-
from rats import apps
|
10
|
-
|
11
|
-
logger = logging.getLogger(__name__)
|
12
|
-
|
13
|
-
|
14
|
-
class ExampleData:
|
15
|
-
"""A simple data source that might come from another package."""
|
16
|
-
|
17
|
-
_num_samples: int
|
18
|
-
|
19
|
-
def __init__(self, num_samples: int) -> None:
|
20
|
-
"""..."""
|
21
|
-
self._num_samples = num_samples
|
22
|
-
|
23
|
-
def fetch(self) -> Iterator[str]:
|
24
|
-
"""Yields random samples."""
|
25
|
-
for i in range(self._num_samples):
|
26
|
-
yield json.dumps({"index": i, "sample": str(uuid.uuid4())})
|
27
|
-
|
28
|
-
|
29
|
-
class ExampleExe(apps.Executable):
|
30
|
-
"""..."""
|
31
|
-
|
32
|
-
_example_data: ExampleData
|
33
|
-
|
34
|
-
def __init__(self, example_data: ExampleData) -> None:
|
35
|
-
"""..."""
|
36
|
-
self._example_data = example_data
|
37
|
-
|
38
|
-
def execute(self) -> None:
|
39
|
-
"""..."""
|
40
|
-
for row in self._example_data.fetch():
|
41
|
-
print(row)
|
42
|
-
|
43
|
-
|
44
|
-
@apps.autoscope
|
45
|
-
class PluginServices:
|
46
|
-
"""..."""
|
47
|
-
|
48
|
-
MAIN_EXE = apps.ServiceId[apps.Executable]("main")
|
49
|
-
EXAMPLE_DATA = apps.ServiceId[ExampleData]("example-data")
|
50
|
-
|
51
|
-
|
52
|
-
class PluginContainer(apps.Container):
|
53
|
-
"""..."""
|
54
|
-
|
55
|
-
_app: apps.Container
|
56
|
-
|
57
|
-
def __init__(self, app: apps.Container) -> None:
|
58
|
-
"""..."""
|
59
|
-
self._app = app
|
60
|
-
|
61
|
-
@apps.service(PluginServices.MAIN_EXE)
|
62
|
-
def _main_exe(self) -> apps.Executable:
|
63
|
-
"""..."""
|
64
|
-
return ExampleExe(
|
65
|
-
example_data=self._app.get(PluginServices.EXAMPLE_DATA),
|
66
|
-
)
|
67
|
-
|
68
|
-
@apps.service(PluginServices.EXAMPLE_DATA)
|
69
|
-
def _example_data(self) -> ExampleData:
|
70
|
-
"""..."""
|
71
|
-
return ExampleData(
|
72
|
-
num_samples=int(os.environ.get("EXAMPLE_DATA_NUM_SAMPLES", "5")),
|
73
|
-
)
|
74
|
-
|
75
|
-
|
76
|
-
if __name__ == "__main__":
|
77
|
-
apps.SimpleApplication(runtime_plugin=PluginContainer).execute(
|
78
|
-
PluginServices.MAIN_EXE,
|
79
|
-
)
|
rats/apps/_plugins.py
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
import warnings
|
2
|
-
from collections.abc import Callable, Iterator
|
3
|
-
from typing import Generic, TypeVar
|
4
|
-
|
5
|
-
T_PluginType = TypeVar("T_PluginType")
|
6
|
-
|
7
|
-
|
8
|
-
class PluginRunner(Generic[T_PluginType]):
|
9
|
-
"""Client to apply a function to a list of plugins."""
|
10
|
-
|
11
|
-
_plugins: Iterator[T_PluginType]
|
12
|
-
|
13
|
-
def __init__(self, plugins: Iterator[T_PluginType]) -> None:
|
14
|
-
self._plugins = plugins
|
15
|
-
|
16
|
-
def apply(self, handler: Callable[[T_PluginType], None]) -> None:
|
17
|
-
warnings.warn(
|
18
|
-
"PluginRunner is deprecated. Use PluginContainer instances instead.",
|
19
|
-
DeprecationWarning,
|
20
|
-
stacklevel=2,
|
21
|
-
)
|
22
|
-
for plugin in self._plugins:
|
23
|
-
handler(plugin)
|
rats/apps/_simple_apps.py
DELETED
@@ -1,163 +0,0 @@
|
|
1
|
-
from collections.abc import Callable, Iterable, Iterator
|
2
|
-
from contextlib import contextmanager
|
3
|
-
from typing import final
|
4
|
-
|
5
|
-
from ._annotations import fallback_service, service
|
6
|
-
from ._composite_container import CompositeContainer
|
7
|
-
from ._container import Container, container
|
8
|
-
from ._executables import App, Executable
|
9
|
-
from ._ids import ServiceId
|
10
|
-
from ._plugin_container import PluginContainers
|
11
|
-
from ._runtimes import Runtime, T_ExecutableType
|
12
|
-
from ._scoping import autoscope
|
13
|
-
|
14
|
-
|
15
|
-
class ExecutableCallableContext(Executable):
|
16
|
-
"""
|
17
|
-
An executable that can be set dynamically with a callable.
|
18
|
-
|
19
|
-
We use this class to support the use of `rats.apps` with a plain callable, like is expected of
|
20
|
-
standard python scripts. We give this class a service id, and set the callable before using
|
21
|
-
`apps.Runtime` to execute the chosen service id.
|
22
|
-
"""
|
23
|
-
|
24
|
-
_exe_id: ServiceId[Executable]
|
25
|
-
_callables: list[Callable[[], None]]
|
26
|
-
|
27
|
-
def __init__(self, exe_id: ServiceId[Executable]) -> None:
|
28
|
-
self._exe_id = exe_id
|
29
|
-
self._callables = []
|
30
|
-
|
31
|
-
def execute(self) -> None:
|
32
|
-
if len(self._callables) == 0:
|
33
|
-
raise RuntimeError("No active executable found.")
|
34
|
-
|
35
|
-
self._callables[-1]()
|
36
|
-
|
37
|
-
@contextmanager
|
38
|
-
def open_callable(
|
39
|
-
self,
|
40
|
-
callable: Callable[[], None],
|
41
|
-
) -> Iterator[Executable]:
|
42
|
-
self._callables.append(callable)
|
43
|
-
try:
|
44
|
-
yield App(callable)
|
45
|
-
finally:
|
46
|
-
self._callables.pop()
|
47
|
-
|
48
|
-
|
49
|
-
@autoscope
|
50
|
-
class AppServices:
|
51
|
-
"""
|
52
|
-
Services used by simple apps that can generally be used anywhere.
|
53
|
-
|
54
|
-
Owners of applications can decide not to use or not to make these services available to plugin
|
55
|
-
authors.
|
56
|
-
"""
|
57
|
-
|
58
|
-
RUNTIME = ServiceId[Runtime]("app-runtime")
|
59
|
-
STANDARD_RUNTIME = ServiceId[Runtime]("standard-runtime")
|
60
|
-
CONTAINER = ServiceId[Container]("app-container")
|
61
|
-
CALLABLE_EXE_CTX = ServiceId[ExecutableCallableContext]("callable-exe-ctx")
|
62
|
-
CALLABLE_EXE = ServiceId[Executable]("callable-exe")
|
63
|
-
|
64
|
-
|
65
|
-
@final
|
66
|
-
class StandardRuntime(Runtime):
|
67
|
-
"""A simple runtime that executes sequentially and in a single thread."""
|
68
|
-
|
69
|
-
_app: Container
|
70
|
-
|
71
|
-
def __init__(self, app: Container) -> None:
|
72
|
-
self._app = app
|
73
|
-
|
74
|
-
def execute(self, *exe_ids: ServiceId[T_ExecutableType]) -> None:
|
75
|
-
for exe_id in exe_ids:
|
76
|
-
self._app.get(exe_id).execute()
|
77
|
-
|
78
|
-
def execute_group(self, *exe_group_ids: ServiceId[T_ExecutableType]) -> None:
|
79
|
-
for exe_group_id in exe_group_ids:
|
80
|
-
for exe in self._app.get_group(exe_group_id):
|
81
|
-
exe.execute()
|
82
|
-
|
83
|
-
def execute_callable(self, *callables: Callable[[], None]) -> None:
|
84
|
-
ctx: ExecutableCallableContext = self._app.get(AppServices.CALLABLE_EXE_CTX)
|
85
|
-
for cb in callables:
|
86
|
-
with ctx.open_callable(cb) as exe:
|
87
|
-
exe.execute()
|
88
|
-
|
89
|
-
|
90
|
-
@final
|
91
|
-
class SimpleApplication(Runtime, Container):
|
92
|
-
"""An application without anything fancy."""
|
93
|
-
|
94
|
-
_plugin_groups: Iterable[str]
|
95
|
-
_runtime_plugin: Callable[[Container], Container] | None
|
96
|
-
|
97
|
-
def __init__(
|
98
|
-
self,
|
99
|
-
*plugin_groups: str,
|
100
|
-
runtime_plugin: Callable[[Container], Container] | None = None,
|
101
|
-
) -> None:
|
102
|
-
self._plugin_groups = plugin_groups
|
103
|
-
self._runtime_plugin = runtime_plugin
|
104
|
-
|
105
|
-
def execute(self, *exe_ids: ServiceId[T_ExecutableType]) -> None:
|
106
|
-
self._runtime().execute(*exe_ids)
|
107
|
-
|
108
|
-
def execute_group(self, *exe_group_ids: ServiceId[T_ExecutableType]) -> None:
|
109
|
-
self._runtime().execute_group(*exe_group_ids)
|
110
|
-
|
111
|
-
def execute_callable(self, *callables: Callable[[], None]) -> None:
|
112
|
-
for cb in callables:
|
113
|
-
self._runtime().execute_callable(cb)
|
114
|
-
|
115
|
-
@fallback_service(AppServices.RUNTIME)
|
116
|
-
def _runtime(self) -> Runtime:
|
117
|
-
"""
|
118
|
-
The default runtime defers to AppServices.STANDARD_RUNTIME.
|
119
|
-
|
120
|
-
Define a non-fallback service to override this default implementation.
|
121
|
-
"""
|
122
|
-
return self.get(AppServices.STANDARD_RUNTIME)
|
123
|
-
|
124
|
-
@service(AppServices.STANDARD_RUNTIME)
|
125
|
-
def _std_runtime(self) -> Runtime:
|
126
|
-
"""
|
127
|
-
The standard locally executed runtime.
|
128
|
-
|
129
|
-
Regardless of the configured AppServices.RUNTIME provider, everyone has access to this
|
130
|
-
class for plugin development.
|
131
|
-
"""
|
132
|
-
return StandardRuntime(self)
|
133
|
-
|
134
|
-
@fallback_service(AppServices.CALLABLE_EXE)
|
135
|
-
def _callable_exe(self) -> Executable:
|
136
|
-
"""We use the callable exe ctx here, so we can treat it like any other app downstream."""
|
137
|
-
return self.get(AppServices.CALLABLE_EXE_CTX)
|
138
|
-
|
139
|
-
@fallback_service(AppServices.CALLABLE_EXE_CTX)
|
140
|
-
def _callable_exe_ctx(self) -> ExecutableCallableContext:
|
141
|
-
"""
|
142
|
-
The default executable context client for executing raw callables.
|
143
|
-
|
144
|
-
Define a non-fallback service to override this default implementation.
|
145
|
-
"""
|
146
|
-
return ExecutableCallableContext(AppServices.CALLABLE_EXE)
|
147
|
-
|
148
|
-
@fallback_service(AppServices.CONTAINER)
|
149
|
-
def _container(self) -> Container:
|
150
|
-
"""
|
151
|
-
The default container is the root application instance.
|
152
|
-
|
153
|
-
Define a non-fallback service to override this default implementation.
|
154
|
-
"""
|
155
|
-
return self
|
156
|
-
|
157
|
-
@container()
|
158
|
-
def _plugins(self) -> Container:
|
159
|
-
plugins: list[Container] = [PluginContainers(self, group) for group in self._plugin_groups]
|
160
|
-
if self._runtime_plugin:
|
161
|
-
plugins.append(self._runtime_plugin(self))
|
162
|
-
|
163
|
-
return CompositeContainer(*plugins)
|
rats/logs/_plugin.py
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
import logging.config
|
2
|
-
from collections.abc import Iterator
|
3
|
-
|
4
|
-
from rats import apps
|
5
|
-
|
6
|
-
logger = logging.getLogger(__name__)
|
7
|
-
|
8
|
-
|
9
|
-
@apps.autoscope
|
10
|
-
class _PluginEvents:
|
11
|
-
CONFIGURE_LOGGING = apps.ServiceId[apps.Executable]("configure-logging")
|
12
|
-
|
13
|
-
|
14
|
-
@apps.autoscope
|
15
|
-
class PluginServices:
|
16
|
-
EVENTS = _PluginEvents
|
17
|
-
|
18
|
-
|
19
|
-
class PluginContainer(apps.Container):
|
20
|
-
_app: apps.Container
|
21
|
-
|
22
|
-
def __init__(self, app: apps.Container) -> None:
|
23
|
-
self._app = app
|
24
|
-
|
25
|
-
@apps.group(PluginServices.EVENTS.CONFIGURE_LOGGING)
|
26
|
-
def _configure_logging(self) -> Iterator[apps.Executable]:
|
27
|
-
# in the future, we can use this plugin to make logging easily configurable
|
28
|
-
yield apps.App(
|
29
|
-
lambda: logging.config.dictConfig(
|
30
|
-
{
|
31
|
-
"version": 1,
|
32
|
-
"disable_existing_loggers": False,
|
33
|
-
"formatters": {
|
34
|
-
"colored": {
|
35
|
-
"()": "colorlog.ColoredFormatter",
|
36
|
-
"format": (
|
37
|
-
"%(log_color)s%(asctime)s %(levelname)-8s [%(name)s][%(lineno)d]: "
|
38
|
-
"%(message)s%(reset)s"
|
39
|
-
),
|
40
|
-
"datefmt": "%Y-%m-%d %H:%M:%S",
|
41
|
-
"log_colors": {
|
42
|
-
"DEBUG": "white",
|
43
|
-
"INFO": "green",
|
44
|
-
"WARNING": "yellow",
|
45
|
-
"ERROR": "red,",
|
46
|
-
"CRITICAL": "bold_red",
|
47
|
-
},
|
48
|
-
}
|
49
|
-
},
|
50
|
-
"handlers": {
|
51
|
-
"console": {
|
52
|
-
"class": "logging.StreamHandler",
|
53
|
-
"level": "DEBUG",
|
54
|
-
"formatter": "colored",
|
55
|
-
"stream": "ext://sys.stderr",
|
56
|
-
}
|
57
|
-
},
|
58
|
-
"loggers": {
|
59
|
-
"": {"level": "INFO", "handlers": ["console"]},
|
60
|
-
"azure": {"level": "WARNING", "handlers": ["console"]},
|
61
|
-
},
|
62
|
-
}
|
63
|
-
)
|
64
|
-
)
|
@@ -1,33 +0,0 @@
|
|
1
|
-
rats/annotations/__init__.py,sha256=wsGhRQzZrV2oJTnBAX0aGgpyT1kYT235jkP3Wb8BTRY,498
|
2
|
-
rats/annotations/__main__.py,sha256=vlzQOM9y82P0OL5tYcmSM_4jTg0s8jayAcvEoi9cBvI,1065
|
3
|
-
rats/annotations/_functions.py,sha256=UkHh3zdBivluE7dBeGQ17zoIfGdyIokMAkFmpWaIlDc,4284
|
4
|
-
rats/annotations/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
rats/apps/__init__.py,sha256=1S5GcOE7aQQxRD2fJEpo6HipTi7sJgVx-RpVdcucOwY,1743
|
6
|
-
rats/apps/__main__.py,sha256=KjdadN4rdP0xhWiLzdmtCsXejWx_gxOK-ah-L1r1dTI,1818
|
7
|
-
rats/apps/_annotations.py,sha256=6M_M7K8haNVda0Tx02EpFf3s9EjnWYacNMjTIkNEdRU,4617
|
8
|
-
rats/apps/_composite_container.py,sha256=s_of6NyyrjFVYWGVehyEHe9WJIPRCnbB-tyWyNF8zyc,585
|
9
|
-
rats/apps/_container.py,sha256=Gb7jJmGyKGDZFjoe0s0C_gC7ymSpXaqordfQaJZGJf4,7283
|
10
|
-
rats/apps/_executables.py,sha256=QJ5_UPdZPmDQ1a3cLRJDUoeUMzNMwa3ZHYhYeS3AVq4,971
|
11
|
-
rats/apps/_ids.py,sha256=T8Onrj79t8NPfBMQBk0xI6fIWDKF0m2JfFNrdtXAbWg,353
|
12
|
-
rats/apps/_namespaces.py,sha256=THUV_Xj5PtweC23Ob-zsSpk8exC4fT-qRwjpQ6IDm0U,188
|
13
|
-
rats/apps/_plugin_container.py,sha256=wmaBgxmvKo82ue9CrKHRXafgik5wIXh8XkEYMfhcTjs,1530
|
14
|
-
rats/apps/_plugins.py,sha256=mvSYQPi11wTGZpM4umoyD37Rc8CQX8nt5eAJbmLrBFM,688
|
15
|
-
rats/apps/_runtimes.py,sha256=qKhsuaH3ZesSP4pGwRbS8zj2mwapysSxyS_F9pkUtM4,1738
|
16
|
-
rats/apps/_scoping.py,sha256=6C2-ID22cCPR9Cbexf3CvCF3o9F_7ieURbwqkf6DI68,1360
|
17
|
-
rats/apps/_simple_apps.py,sha256=n-3zeHY3iintZ9LN597c7zDHv3DiIdl7c8NTk0gUk1Y,5477
|
18
|
-
rats/apps/_static_container.py,sha256=5lzLh1CUoQVwhxfDhK6ZOQaBrPpudbRHWJkaDppphZo,881
|
19
|
-
rats/apps/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
-
rats/cli/__init__.py,sha256=GR_pZr0OtqkH2js73OPZlK4Pgj0C30bhSKPGfU32Zxk,580
|
21
|
-
rats/cli/__main__.py,sha256=5OidbkRA4aglOAcvV6xQY9k7Ls9vd_EwNvI2aOrcPIQ,1649
|
22
|
-
rats/cli/_annotations.py,sha256=5voM1pNm7iybZpgQTSVpaq3rMIMz33jr4eUyoEmxWJ4,1359
|
23
|
-
rats/cli/_app.py,sha256=EmLIJe1LTLob5u5gP8gpgxk3kFlALTvrpYlwxlILu0k,620
|
24
|
-
rats/cli/_container.py,sha256=q3L6On6RS3PfUFZ67t6eUvKUQIK4Qxomz5ob86aNcR4,2143
|
25
|
-
rats/cli/_plugin.py,sha256=jkpqSCW0ELnMjK8fz-BVE50qgBIxbd5j4hILOnlla6U,1086
|
26
|
-
rats/cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
|
-
rats/logs/__init__.py,sha256=fCn4pfpYiAcTtt5CsnUZX68CjOB3KJHxMSiYxsma4qE,183
|
28
|
-
rats/logs/_plugin.py,sha256=OQ5fXToBm60YJRrMUVJ9_HVytUs3c69vaHY57jpivb0,2221
|
29
|
-
rats/logs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
|
-
rats_apps-0.4.0.dev20241212213929.dist-info/METADATA,sha256=Sug-FUXtluJL-S6QzI7-dz9uhMjmA7p_FxdYsh2Xm1M,825
|
31
|
-
rats_apps-0.4.0.dev20241212213929.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
32
|
-
rats_apps-0.4.0.dev20241212213929.dist-info/entry_points.txt,sha256=9oOvf2loQr5ACWQgvuu9Q3KZIVIxKE5Aa-rLuUII5WQ,91
|
33
|
-
rats_apps-0.4.0.dev20241212213929.dist-info/RECORD,,
|
{rats_apps-0.4.0.dev20241212213929.dist-info → rats_apps-0.5.0.dev20250102200830.dist-info}/WHEEL
RENAMED
File without changes
|