The infrastructure consists of the message bus from module messagebus, the
plugin registry from module pluginregistry and the abstract base plugin from
-the module baseplugin.
+module baseplugin.
-The package combines them in its run function.
+The package combines them in its run function, which is used by __main__.py
+to run a ControlPi system based on a configuration file indefinitely.
+
+The test function is a utility function to test plugins with minimal
+boilerplate code.
"""
import asyncio
import jsonschema # type: ignore
from typing import Dict, List, Coroutine, Any
+
CONF_SCHEMA = {'type': 'object',
'patternProperties': {'.*': {'type': 'object'}}}
Setup message bus, process given configuration, and run message bus and
plugins concurrently and indefinitely.
- TODO: doctests for run using util.py
+ This function is mainly used by __main__.py to run a ControlPi system
+ based on a configuration loaded from a configuration JSON file on disk.
+
+ >>> async def test_coroutine():
+ ... conf = {"Example Init":
+ ... {"plugin": "Init",
+ ... "messages": [{"id": 42,
+ ... "content": "Test Message"},
+ ... {"id": 42.42,
+ ... "content": "Second Message"}]},
+ ... "Example Log":
+ ... {"plugin": "Log",
+ ... "filter": [{"sender": {"const": "Example Init"}}]}}
+ ... run_task = asyncio.create_task(run(conf))
+ ... await asyncio.sleep(0.1)
+ ... run_task.cancel()
+ >>> asyncio.run(test_coroutine()) # doctest: +NORMALIZE_WHITESPACE
+ Example Log: {'sender': 'Example Init',
+ 'id': 42, 'content': 'Test Message'}
+ Example Log: {'sender': 'Example Init',
+ 'id': 42.42, 'content': 'Second Message'}
"""
message_bus = MessageBus()
coroutines = _process_conf(message_bus, conf)
plugins concurrently, send given messages on message bus and print all
messages on message bus. Terminate when queue of message bus is empty.
+ This function allows to test single plugins or small plugin
+ configurations with minimal boilerplate code:
>>> asyncio.run(test(
... {"Example Init": {"plugin": "Init",
... "messages": [{"id": 42,
'sends': [{'id': {'const': 42},
'content': {'const': 'Test Message'}},
{'id': {'const': 42.42},
- 'content': {'const': 'Second Message'}},
- {'target': {'const': 'Example Init'},
- 'command': {'const': 'execute'}}],
+ 'content': {'const': 'Second Message'}}],
'receives': [{'target': {'const': 'Example Init'},
'command': {'const': 'execute'}}]}
- test(): {'sender': 'Example Init', 'target': 'Example Init',
- 'command': 'execute'}
- test(): {'sender': 'test()', 'target': 'Example Init',
- 'command': 'execute'}
test(): {'sender': 'Example Init',
'id': 42, 'content': 'Test Message'}
test(): {'sender': 'Example Init',
'id': 42.42, 'content': 'Second Message'}
+ test(): {'sender': 'test()', 'target': 'Example Init',
+ 'command': 'execute'}
test(): {'sender': 'Example Init',
'id': 42, 'content': 'Test Message'}
test(): {'sender': 'Example Init',
'id': 42.42, 'content': 'Second Message'}
+
+ Similar functionality could be reached by using the Log and Init plugins
+ to print messages and send some messages on the bus, but these would
+ clutter the test configuration and code to stop the idefinitely running
+ bus would have to be added to each and every test.
+
+ Incorrect plugin configurations can also be tested by this:
+ >>> asyncio.run(test(
+ ... {"Example Init": {"plugin": "Init"}}, []))
+ 'messages' is a required property
+ <BLANKLINE>
+ Failed validating 'required' in schema:
+ {'properties': {'messages': {'type': 'array'}},
+ 'required': ['messages']}
+ <BLANKLINE>
+ On instance:
+ {'plugin': 'Init'}
+ Configuration for 'Example Init' is not valid.
"""
message_bus = MessageBus()