parallelazy 0.0.1__tar.gz → 0.0.2__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.
- parallelazy-0.0.2/PKG-INFO +82 -0
- parallelazy-0.0.2/README.md +67 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/plugin_data.py +1 -1
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/plugin_manager.py +6 -6
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/plugin_rpc.py +2 -2
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/plugin_transport.py +1 -1
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/plugin_worker.py +4 -4
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/utils.py +1 -1
- parallelazy-0.0.2/parallelazy.egg-info/PKG-INFO +82 -0
- parallelazy-0.0.2/parallelazy.egg-info/SOURCES.txt +17 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy.egg-info/requires.txt +1 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/pyproject.toml +3 -2
- parallelazy-0.0.1/PKG-INFO +0 -17
- parallelazy-0.0.1/README.md +0 -3
- parallelazy-0.0.1/parallelazy/examples/__init__.py +0 -0
- parallelazy-0.0.1/parallelazy/examples/example_configuration.py +0 -194
- parallelazy-0.0.1/parallelazy/examples/example_count.py +0 -84
- parallelazy-0.0.1/parallelazy/examples/example_data_pipeline.py +0 -226
- parallelazy-0.0.1/parallelazy/examples/example_env_variables.py +0 -158
- parallelazy-0.0.1/parallelazy/examples/example_error_handling.py +0 -266
- parallelazy-0.0.1/parallelazy/examples/example_graceful_shutdown.py +0 -139
- parallelazy-0.0.1/parallelazy/examples/example_ml_training.py +0 -280
- parallelazy-0.0.1/parallelazy/examples/example_pubsub.py +0 -137
- parallelazy-0.0.1/parallelazy/examples/example_rpc_calls.py +0 -182
- parallelazy-0.0.1/parallelazy/examples/example_worker_pool.py +0 -188
- parallelazy-0.0.1/parallelazy/tests/__init__.py +0 -0
- parallelazy-0.0.1/parallelazy.egg-info/PKG-INFO +0 -17
- parallelazy-0.0.1/parallelazy.egg-info/SOURCES.txt +0 -29
- {parallelazy-0.0.1 → parallelazy-0.0.2}/LICENSE +0 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/__init__.py +0 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/environment_manager.py +0 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy/plugin.py +0 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy.egg-info/dependency_links.txt +0 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/parallelazy.egg-info/top_level.txt +0 -0
- {parallelazy-0.0.1 → parallelazy-0.0.2}/setup.cfg +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: parallelazy
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: A lightweight multiprocess worker-plugin framework. Topic-based pub/sub, global enviroment, using process spawn and pipes, pytorch and CUDA ready.
|
|
5
|
+
Author: Cesare Montresor
|
|
6
|
+
Project-URL: Homepage, https://github.com/cesare-montresor/parallelazy
|
|
7
|
+
Project-URL: Repository, https://github.com/cesare-montresor/parallelazy
|
|
8
|
+
Project-URL: Issues, https://github.com/cesare-montresor/parallelazy/issues
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: debugpy
|
|
13
|
+
Requires-Dist: torch
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# parallelazy
|
|
17
|
+
|
|
18
|
+
A lightweight Python plugin/worker framework with:
|
|
19
|
+
|
|
20
|
+
- isolated plugin processes started with `spawn`;
|
|
21
|
+
- topic-based publish/subscribe;
|
|
22
|
+
- shared environment values and change notifications;
|
|
23
|
+
- plugin-to-plugin RPC; and
|
|
24
|
+
- PyTorch/CUDA-aware multiprocessing support.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python -m pip install .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import time
|
|
36
|
+
|
|
37
|
+
from parallelazy.plugin import Plugin
|
|
38
|
+
from parallelazy.plugin_manager import PluginManager
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Worker(Plugin):
|
|
42
|
+
def on_start(self):
|
|
43
|
+
self.env_set("worker.ready", True)
|
|
44
|
+
|
|
45
|
+
def main(self):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main():
|
|
50
|
+
manager = PluginManager()
|
|
51
|
+
descriptor = Worker.Descriptor(
|
|
52
|
+
version="1.0.0",
|
|
53
|
+
author="Application team",
|
|
54
|
+
description="Example worker",
|
|
55
|
+
)
|
|
56
|
+
if not manager.load_plugin(descriptor):
|
|
57
|
+
raise RuntimeError("Plugin failed to load")
|
|
58
|
+
if not manager.start_plugin(descriptor.name):
|
|
59
|
+
raise RuntimeError("Plugin failed to start")
|
|
60
|
+
try:
|
|
61
|
+
time.sleep(0.1)
|
|
62
|
+
print(manager.env_get("worker.ready"))
|
|
63
|
+
finally:
|
|
64
|
+
manager.unload_plugin(descriptor.name)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
main()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
Read the [documentation index](docs/index.md) for installation, concepts,
|
|
74
|
+
API reference, usage patterns, and troubleshooting.
|
|
75
|
+
|
|
76
|
+
The complete runnable examples are in
|
|
77
|
+
[examples/](examples/). For example:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python -m examples.example_pubsub
|
|
81
|
+
python -m examples.example_rpc_calls
|
|
82
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# parallelazy
|
|
2
|
+
|
|
3
|
+
A lightweight Python plugin/worker framework with:
|
|
4
|
+
|
|
5
|
+
- isolated plugin processes started with `spawn`;
|
|
6
|
+
- topic-based publish/subscribe;
|
|
7
|
+
- shared environment values and change notifications;
|
|
8
|
+
- plugin-to-plugin RPC; and
|
|
9
|
+
- PyTorch/CUDA-aware multiprocessing support.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python -m pip install .
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import time
|
|
21
|
+
|
|
22
|
+
from parallelazy.plugin import Plugin
|
|
23
|
+
from parallelazy.plugin_manager import PluginManager
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Worker(Plugin):
|
|
27
|
+
def on_start(self):
|
|
28
|
+
self.env_set("worker.ready", True)
|
|
29
|
+
|
|
30
|
+
def main(self):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main():
|
|
35
|
+
manager = PluginManager()
|
|
36
|
+
descriptor = Worker.Descriptor(
|
|
37
|
+
version="1.0.0",
|
|
38
|
+
author="Application team",
|
|
39
|
+
description="Example worker",
|
|
40
|
+
)
|
|
41
|
+
if not manager.load_plugin(descriptor):
|
|
42
|
+
raise RuntimeError("Plugin failed to load")
|
|
43
|
+
if not manager.start_plugin(descriptor.name):
|
|
44
|
+
raise RuntimeError("Plugin failed to start")
|
|
45
|
+
try:
|
|
46
|
+
time.sleep(0.1)
|
|
47
|
+
print(manager.env_get("worker.ready"))
|
|
48
|
+
finally:
|
|
49
|
+
manager.unload_plugin(descriptor.name)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
main()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Documentation
|
|
57
|
+
|
|
58
|
+
Read the [documentation index](docs/index.md) for installation, concepts,
|
|
59
|
+
API reference, usage patterns, and troubleshooting.
|
|
60
|
+
|
|
61
|
+
The complete runnable examples are in
|
|
62
|
+
[examples/](examples/). For example:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
python -m examples.example_pubsub
|
|
66
|
+
python -m examples.example_rpc_calls
|
|
67
|
+
```
|
|
@@ -13,12 +13,12 @@ import torch.multiprocessing as mp
|
|
|
13
13
|
import time
|
|
14
14
|
from typing import Any, Dict, List, Optional
|
|
15
15
|
|
|
16
|
-
from parallelazy.utils
|
|
17
|
-
from parallelazy.plugin
|
|
18
|
-
from parallelazy.
|
|
19
|
-
from parallelazy.
|
|
20
|
-
from parallelazy.
|
|
21
|
-
from parallelazy.
|
|
16
|
+
from parallelazy.utils import process_start_spawn
|
|
17
|
+
from parallelazy.plugin import PluginDescriptor
|
|
18
|
+
from parallelazy.plugin_rpc import PluginRPC, RequestEnvGetAll, RequestEnvUnsubscribe, RequestTopicAnnounce
|
|
19
|
+
from parallelazy.plugin_worker import PluginWorker, Queue, RequestPluginCall, RequestTopicSubscribe, RequestTopicUnsubscribe
|
|
20
|
+
from parallelazy.plugin_data import CommandType, ProcessStatus, RequestEnvGet, RequestEnvSet, RequestEnvSubscribe, PluginStatus, Subscriber
|
|
21
|
+
from parallelazy.environment_manager import EnvironmentManager
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
@@ -8,8 +8,8 @@ from torch import multiprocessing as mp
|
|
|
8
8
|
import threading
|
|
9
9
|
from concurrent.futures import ThreadPoolExecutor
|
|
10
10
|
from multiprocessing.connection import wait
|
|
11
|
-
from parallelazy.
|
|
12
|
-
from parallelazy.
|
|
11
|
+
from parallelazy.plugin_data import Channel, CommandType, Message, PendingRequest, Queue, Request, RequestCallback, RequestEnvGet, RequestEnvGetAll, RequestEnvSet, RequestEnvSubscribe, RequestEnvUnsubscribe, RequestEnvUpdate, RequestPluginCall, RequestPluginErrors, RequestPluginStart, RequestPluginStatus, RequestPluginStatusAll, RequestPluginStop, RequestTopicAnnounce, RequestTopicList, RequestTopicSubscribe, RequestTopicUnsubscribe, Response, Subscriber
|
|
12
|
+
from parallelazy.plugin_transport import sanitize_for_ipc
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
@@ -18,11 +18,11 @@ import threading
|
|
|
18
18
|
import time
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
from parallelazy.utils
|
|
22
|
-
from parallelazy.
|
|
23
|
-
from parallelazy.plugin
|
|
21
|
+
from parallelazy.utils import init_debugger, process_start_spawn
|
|
22
|
+
from parallelazy.plugin_data import Channel, CommandType, Queue,QueueDescriptor, PendingRequest, PluginStatus, RequestPluginErrors, RequestPluginStart, RequestPluginStatus, RequestPluginStop, RequestTopicList, Subscriber, Subscription
|
|
23
|
+
from parallelazy.plugin import Plugin, PluginDescriptor, PluginState
|
|
24
24
|
|
|
25
|
-
from parallelazy.
|
|
25
|
+
from parallelazy.plugin_rpc import PluginRPC, RequestEnvUpdate, RequestPluginCall, RequestTopicList, RequestTopicSubscribe, RequestTopicUnsubscribe, sanitize_for_ipc
|
|
26
26
|
from multiprocessing.synchronize import Event
|
|
27
27
|
|
|
28
28
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: parallelazy
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: A lightweight multiprocess worker-plugin framework. Topic-based pub/sub, global enviroment, using process spawn and pipes, pytorch and CUDA ready.
|
|
5
|
+
Author: Cesare Montresor
|
|
6
|
+
Project-URL: Homepage, https://github.com/cesare-montresor/parallelazy
|
|
7
|
+
Project-URL: Repository, https://github.com/cesare-montresor/parallelazy
|
|
8
|
+
Project-URL: Issues, https://github.com/cesare-montresor/parallelazy/issues
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: debugpy
|
|
13
|
+
Requires-Dist: torch
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# parallelazy
|
|
17
|
+
|
|
18
|
+
A lightweight Python plugin/worker framework with:
|
|
19
|
+
|
|
20
|
+
- isolated plugin processes started with `spawn`;
|
|
21
|
+
- topic-based publish/subscribe;
|
|
22
|
+
- shared environment values and change notifications;
|
|
23
|
+
- plugin-to-plugin RPC; and
|
|
24
|
+
- PyTorch/CUDA-aware multiprocessing support.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python -m pip install .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import time
|
|
36
|
+
|
|
37
|
+
from parallelazy.plugin import Plugin
|
|
38
|
+
from parallelazy.plugin_manager import PluginManager
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Worker(Plugin):
|
|
42
|
+
def on_start(self):
|
|
43
|
+
self.env_set("worker.ready", True)
|
|
44
|
+
|
|
45
|
+
def main(self):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main():
|
|
50
|
+
manager = PluginManager()
|
|
51
|
+
descriptor = Worker.Descriptor(
|
|
52
|
+
version="1.0.0",
|
|
53
|
+
author="Application team",
|
|
54
|
+
description="Example worker",
|
|
55
|
+
)
|
|
56
|
+
if not manager.load_plugin(descriptor):
|
|
57
|
+
raise RuntimeError("Plugin failed to load")
|
|
58
|
+
if not manager.start_plugin(descriptor.name):
|
|
59
|
+
raise RuntimeError("Plugin failed to start")
|
|
60
|
+
try:
|
|
61
|
+
time.sleep(0.1)
|
|
62
|
+
print(manager.env_get("worker.ready"))
|
|
63
|
+
finally:
|
|
64
|
+
manager.unload_plugin(descriptor.name)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
main()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
Read the [documentation index](docs/index.md) for installation, concepts,
|
|
74
|
+
API reference, usage patterns, and troubleshooting.
|
|
75
|
+
|
|
76
|
+
The complete runnable examples are in
|
|
77
|
+
[examples/](examples/). For example:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python -m examples.example_pubsub
|
|
81
|
+
python -m examples.example_rpc_calls
|
|
82
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
parallelazy/__init__.py
|
|
5
|
+
parallelazy/environment_manager.py
|
|
6
|
+
parallelazy/plugin.py
|
|
7
|
+
parallelazy/plugin_data.py
|
|
8
|
+
parallelazy/plugin_manager.py
|
|
9
|
+
parallelazy/plugin_rpc.py
|
|
10
|
+
parallelazy/plugin_transport.py
|
|
11
|
+
parallelazy/plugin_worker.py
|
|
12
|
+
parallelazy/utils.py
|
|
13
|
+
parallelazy.egg-info/PKG-INFO
|
|
14
|
+
parallelazy.egg-info/SOURCES.txt
|
|
15
|
+
parallelazy.egg-info/dependency_links.txt
|
|
16
|
+
parallelazy.egg-info/requires.txt
|
|
17
|
+
parallelazy.egg-info/top_level.txt
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "parallelazy"
|
|
7
|
-
version = "0.0.
|
|
8
|
-
description = "A lightweight multiprocess worker-plugin framework. Topic-based pub/sub,
|
|
7
|
+
version = "0.0.2"
|
|
8
|
+
description = "A lightweight multiprocess worker-plugin framework. Topic-based pub/sub, global enviroment, using process spawn and pipes, pytorch and CUDA ready."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
11
11
|
authors = [
|
|
@@ -14,6 +14,7 @@ authors = [
|
|
|
14
14
|
|
|
15
15
|
dependencies = [
|
|
16
16
|
"debugpy",
|
|
17
|
+
"torch"
|
|
17
18
|
]
|
|
18
19
|
|
|
19
20
|
|
parallelazy-0.0.1/PKG-INFO
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: parallelazy
|
|
3
|
-
Version: 0.0.1
|
|
4
|
-
Summary: A lightweight multiprocess worker-plugin framework. Topic-based pub/sub, gloval enviroment, using process spawn and pipes, pytorch and CUDA ready.
|
|
5
|
-
Author: Cesare Montresor
|
|
6
|
-
Project-URL: Homepage, https://github.com/cesare-montresor/parallelazy
|
|
7
|
-
Project-URL: Repository, https://github.com/cesare-montresor/parallelazy
|
|
8
|
-
Project-URL: Issues, https://github.com/cesare-montresor/parallelazy/issues
|
|
9
|
-
Requires-Python: >=3.8
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Requires-Dist: debugpy
|
|
13
|
-
Dynamic: license-file
|
|
14
|
-
|
|
15
|
-
# parallelazy
|
|
16
|
-
|
|
17
|
-
A python lightweight, plugin/worker framework, with topic-based pub/sub and shared env using spawn and pipes, pytorch and CUDA ready.
|
parallelazy-0.0.1/README.md
DELETED
|
File without changes
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Configuration & Options Example: Plugin customization at load time.
|
|
3
|
-
|
|
4
|
-
This example shows how plugins can:
|
|
5
|
-
1. Accept configuration options when loaded
|
|
6
|
-
2. Use options to customize behavior
|
|
7
|
-
3. Override configuration dynamically
|
|
8
|
-
4. Validate configuration options
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import logging
|
|
12
|
-
import time
|
|
13
|
-
from typing import Any, Dict
|
|
14
|
-
|
|
15
|
-
from parallelazy.plugin import Plugin, PluginDescriptor
|
|
16
|
-
from parallelazy.plugin_worker import PluginWorker
|
|
17
|
-
from parallelazy.plugin_manager import PluginManager
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class Sensor(Plugin):
|
|
23
|
-
"""
|
|
24
|
-
Plugin that simulates a sensor with configurable sampling.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
@classmethod
|
|
28
|
-
def Descriptor(cls, options: Dict[str, Any] = None) -> PluginDescriptor:
|
|
29
|
-
default_options = {
|
|
30
|
-
"sample_rate": 10, # Hz
|
|
31
|
-
"noise_level": 0.1,
|
|
32
|
-
"sensor_type": "temperature",
|
|
33
|
-
}
|
|
34
|
-
if options:
|
|
35
|
-
default_options.update(options)
|
|
36
|
-
|
|
37
|
-
return super().Descriptor(
|
|
38
|
-
version="1.0.0",
|
|
39
|
-
author="ModularNet Team",
|
|
40
|
-
description="Configurable sensor plugin",
|
|
41
|
-
options=default_options
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
def __init__(self, worker: PluginWorker, plugin_id: str, options: Dict[str, Any]):
|
|
45
|
-
super().__init__(worker, plugin_id, options)
|
|
46
|
-
self.sample_count = 0
|
|
47
|
-
self.sample_rate = options.get("sample_rate", 10)
|
|
48
|
-
self.noise_level = options.get("noise_level", 0.1)
|
|
49
|
-
self.sensor_type = options.get("sensor_type", "temperature")
|
|
50
|
-
|
|
51
|
-
def on_start(self) -> None:
|
|
52
|
-
logger.info(f"[{self.plugin_id}] Sensor started with options:")
|
|
53
|
-
logger.info(f" - type: {self.sensor_type}")
|
|
54
|
-
logger.info(f" - sample_rate: {self.sample_rate} Hz")
|
|
55
|
-
logger.info(f" - noise_level: {self.noise_level}")
|
|
56
|
-
self.env_set("sensor.config", {
|
|
57
|
-
"type": self.sensor_type,
|
|
58
|
-
"sample_rate": self.sample_rate,
|
|
59
|
-
"noise_level": self.noise_level
|
|
60
|
-
})
|
|
61
|
-
self.env_set("sensor.samples", 0)
|
|
62
|
-
|
|
63
|
-
def main(self):
|
|
64
|
-
self.sample_count += 1
|
|
65
|
-
# Generate simulated sensor reading
|
|
66
|
-
base_value = 20 + (self.sample_count % 10) # Temperature 20-30°C
|
|
67
|
-
import random
|
|
68
|
-
noise = random.gauss(0, self.noise_level)
|
|
69
|
-
reading = base_value + noise
|
|
70
|
-
|
|
71
|
-
if self.sample_count % 5 == 0:
|
|
72
|
-
logger.info(f"[{self.plugin_id}] Sample #{self.sample_count}: {reading:.2f}")
|
|
73
|
-
self.topic_publish("sensor.readings", {
|
|
74
|
-
"sample": self.sample_count,
|
|
75
|
-
"value": reading,
|
|
76
|
-
"type": self.sensor_type
|
|
77
|
-
})
|
|
78
|
-
self.env_set("sensor.samples", self.sample_count, wait=False)
|
|
79
|
-
|
|
80
|
-
def on_stop(self) -> None:
|
|
81
|
-
logger.info(f"[{self.plugin_id}] Sensor stopped, collected {self.sample_count} samples")
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
class SensorLogger(Plugin):
|
|
85
|
-
"""
|
|
86
|
-
Plugin that logs sensor readings with configurable verbosity.
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
@classmethod
|
|
90
|
-
def Descriptor(cls, options: Dict[str, Any] = None) -> PluginDescriptor:
|
|
91
|
-
default_options = {
|
|
92
|
-
"verbose": False,
|
|
93
|
-
"log_every_n": 5,
|
|
94
|
-
}
|
|
95
|
-
if options:
|
|
96
|
-
default_options.update(options)
|
|
97
|
-
|
|
98
|
-
return super().Descriptor(
|
|
99
|
-
version="1.0.0",
|
|
100
|
-
author="ModularNet Team",
|
|
101
|
-
description="Sensor logger with configurable output",
|
|
102
|
-
options=default_options
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
def __init__(self, worker: PluginWorker, plugin_id: str, options: Dict[str, Any]):
|
|
106
|
-
super().__init__(worker, plugin_id, options)
|
|
107
|
-
self.verbose = options.get("verbose", False)
|
|
108
|
-
self.log_every_n = options.get("log_every_n", 5)
|
|
109
|
-
self.readings = []
|
|
110
|
-
|
|
111
|
-
def on_start(self) -> None:
|
|
112
|
-
logger.info(f"[{self.plugin_id}] SensorLogger started (verbose={self.verbose})")
|
|
113
|
-
self.topic_subscribe("sensor.readings", self.on_reading)
|
|
114
|
-
|
|
115
|
-
def on_reading(self, reading: Dict[str, Any]):
|
|
116
|
-
self.readings.append(reading)
|
|
117
|
-
if self.verbose:
|
|
118
|
-
logger.info(f"[{self.plugin_id}] Received: {reading}")
|
|
119
|
-
elif len(self.readings) % self.log_every_n == 0:
|
|
120
|
-
avg_value = sum(r["value"] for r in self.readings) / len(self.readings)
|
|
121
|
-
logger.info(f"[{self.plugin_id}] {len(self.readings)} readings, avg={avg_value:.2f}")
|
|
122
|
-
self.env_set("logger.avg_value", avg_value, wait=False)
|
|
123
|
-
|
|
124
|
-
def main(self):
|
|
125
|
-
pass
|
|
126
|
-
|
|
127
|
-
def on_stop(self) -> None:
|
|
128
|
-
logger.info(f"[{self.plugin_id}] SensorLogger stopped, logged {len(self.readings)} readings")
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def main():
|
|
132
|
-
"""Run the configuration example."""
|
|
133
|
-
manager = PluginManager()
|
|
134
|
-
|
|
135
|
-
# Load sensor with custom options
|
|
136
|
-
sensor_desc = Sensor.Descriptor(options={
|
|
137
|
-
"sample_rate": 20,
|
|
138
|
-
"noise_level": 0.5,
|
|
139
|
-
"sensor_type": "humidity"
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
# Load logger with verbose mode
|
|
143
|
-
logger_desc = SensorLogger.Descriptor(options={
|
|
144
|
-
"verbose": False,
|
|
145
|
-
"log_every_n": 3
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
manager.load_plugin(sensor_desc)
|
|
149
|
-
manager.load_plugin(logger_desc)
|
|
150
|
-
|
|
151
|
-
manager.start_plugin(sensor_desc.name)
|
|
152
|
-
manager.start_plugin(logger_desc.name)
|
|
153
|
-
|
|
154
|
-
print("Sensor and logger running... Press Ctrl+C to stop.")
|
|
155
|
-
time.sleep(5)
|
|
156
|
-
print("Stopping plugins...")
|
|
157
|
-
|
|
158
|
-
manager.stop_plugin(logger_desc.name)
|
|
159
|
-
manager.stop_plugin(sensor_desc.name)
|
|
160
|
-
|
|
161
|
-
# Show configuration
|
|
162
|
-
print("\nFinal state:")
|
|
163
|
-
all_vars = manager.env_get()
|
|
164
|
-
for name, value in all_vars.items():
|
|
165
|
-
if name.startswith("sensor.") or name.startswith("logger."):
|
|
166
|
-
print(f" {name}: {value}")
|
|
167
|
-
|
|
168
|
-
# VERIFICATION
|
|
169
|
-
print("\n=== VERIFICATION ===")
|
|
170
|
-
print("PREDICTED BEHAVIOR:")
|
|
171
|
-
print("1. Sensor loads with custom options (sample_rate=20, noise_level=0.5, type=humidity)")
|
|
172
|
-
print("2. Logger loads with verbose=False, log_every_n=3")
|
|
173
|
-
print("3. Sensor publishes readings, logger subscribes and aggregates")
|
|
174
|
-
print("4. Config persists in environment\n")
|
|
175
|
-
|
|
176
|
-
sensor_config = all_vars.get("sensor.config")
|
|
177
|
-
logger_avg = all_vars.get("logger.avg_value")
|
|
178
|
-
|
|
179
|
-
assert sensor_config is not None, "sensor.config should be set"
|
|
180
|
-
assert sensor_config.get("type") == "humidity", f"Sensor type should be humidity, got {sensor_config.get('type')}"
|
|
181
|
-
assert sensor_config.get("sample_rate") == 20, f"Sample rate should be 20, got {sensor_config.get('sample_rate')}"
|
|
182
|
-
assert sensor_config.get("noise_level") == 0.5, f"Noise level should be 0.5, got {sensor_config.get('noise_level')}"
|
|
183
|
-
|
|
184
|
-
print(f"✓ Sensor config: type={sensor_config['type']}, rate={sensor_config['sample_rate']}Hz, noise={sensor_config['noise_level']}")
|
|
185
|
-
if logger_avg is not None:
|
|
186
|
-
print(f"✓ Logger computed average: {logger_avg:.2f}")
|
|
187
|
-
print("✓ All configuration assertions passed!")
|
|
188
|
-
|
|
189
|
-
manager.unload_plugin(sensor_desc.name)
|
|
190
|
-
manager.unload_plugin(logger_desc.name)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if __name__ == "__main__":
|
|
194
|
-
main()
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Basic test plugin for lifecycle testing.
|
|
3
|
-
|
|
4
|
-
A minimal plugin that exercises the load/unload/start/stop/status lifecycle
|
|
5
|
-
without any external dependencies.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import logging
|
|
9
|
-
from typing import Any, Dict
|
|
10
|
-
|
|
11
|
-
from parallelazy.plugin import Plugin, PluginDescriptor
|
|
12
|
-
from parallelazy.plugin_worker import PluginWorker
|
|
13
|
-
import time
|
|
14
|
-
from parallelazy.plugin_manager import PluginManager
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
logger = logging.getLogger(__name__)
|
|
19
|
-
|
|
20
|
-
class ExamplePluginCount(Plugin):
|
|
21
|
-
"""
|
|
22
|
-
Minimal test plugin that exercises core lifecycle.
|
|
23
|
-
|
|
24
|
-
Features:
|
|
25
|
-
- Simple on_start/on_stop implementation
|
|
26
|
-
- Tracks call count
|
|
27
|
-
- Can be used to verify manager lifecycle
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
@classmethod
|
|
31
|
-
def Descriptor(cls, options: Dict[str, Any] = None) -> PluginDescriptor:
|
|
32
|
-
"""Create plugin descriptor."""
|
|
33
|
-
return super().Descriptor(
|
|
34
|
-
version="1.0.0",
|
|
35
|
-
author="ModularNet Team",
|
|
36
|
-
description="Basic test plugin for lifecycle verification",
|
|
37
|
-
options=options or {}
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
def __init__(
|
|
41
|
-
self,
|
|
42
|
-
worker: PluginWorker,
|
|
43
|
-
plugin_id: str,
|
|
44
|
-
options: Dict[str, Any],
|
|
45
|
-
):
|
|
46
|
-
super().__init__(worker, plugin_id, options)
|
|
47
|
-
self.count = None
|
|
48
|
-
|
|
49
|
-
def main(self):
|
|
50
|
-
self.count +=1
|
|
51
|
-
self.env_set("count", self.count, wait=False)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def on_start(self) -> None:
|
|
55
|
-
logger.info(f"[{self.plugin_id}] ExamplePluginCount starting...")
|
|
56
|
-
self.count = 0
|
|
57
|
-
self.env_set("count", self.count)
|
|
58
|
-
|
|
59
|
-
def on_stop(self) -> None:
|
|
60
|
-
logger.info(f"[{self.plugin_id}] Total loop: {self.count}")
|
|
61
|
-
self.env_set("count", self.count)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def main():
|
|
65
|
-
"""Example main function to run the plugin standalone for testing."""
|
|
66
|
-
manager = PluginManager()
|
|
67
|
-
descriptor = ExamplePluginCount.Descriptor()
|
|
68
|
-
manager.load_plugin(descriptor)
|
|
69
|
-
manager.start_plugin(descriptor.name)
|
|
70
|
-
|
|
71
|
-
# Let it run for a few seconds
|
|
72
|
-
time.sleep(3)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
manager.unload_plugin(descriptor.name)
|
|
76
|
-
|
|
77
|
-
all_vars = manager.env_get()
|
|
78
|
-
|
|
79
|
-
print("All environment variables:")
|
|
80
|
-
for name, value in all_vars.items():
|
|
81
|
-
print(f" {name}: {value}")
|
|
82
|
-
|
|
83
|
-
if __name__ == "__main__": main()
|
|
84
|
-
|