From: Benjamin Braatz
Date: Mon, 9 Mar 2026 10:52:55 +0000 (+0100)
Subject: Update API documentation.
X-Git-Tag: v0.3.9
X-Git-Url: http://git.graph-it.com/?a=commitdiff_plain;h=514e80ee035e02bfc162c4edc224d6c2d8f187e3;p=graphit%2Fcontrolpi.git
Update API documentation.
---
diff --git a/doc/api/controlpi.html b/doc/api/controlpi.html
new file mode 100644
index 0000000..855274c
--- /dev/null
+++ b/doc/api/controlpi.html
@@ -0,0 +1,704 @@
+
+
+
+
+
+
+ controlpi API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+controlpi
+
+ Provide the infrastructure for the ControlPi system.
+
+
The infrastructure consists of the message bus from module messagebus, the
+plugin registry from module pluginregistry and the abstract base plugin from
+module baseplugin.
+
+
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.
+
+
+
+
+ View Source
+
+ 1 """Provide the infrastructure for the ControlPi system.
+ 2
+ 3 The infrastructure consists of the message bus from module messagebus, the
+ 4 plugin registry from module pluginregistry and the abstract base plugin from
+ 5 module baseplugin.
+ 6
+ 7 The package combines them in its run function, which is used by __main__.py
+ 8 to run a ControlPi system based on a configuration file indefinitely.
+ 9
+ 10 The test function is a utility function to test plugins with minimal
+ 11 boilerplate code.
+ 12 """
+ 13
+ 14 import asyncio
+ 15 import fastjsonschema
+ 16
+ 17 from controlpi.messagebus import MessageBus , Message , MessageTemplate
+ 18 from controlpi.pluginregistry import PluginRegistry
+ 19 from controlpi.baseplugin import BasePlugin , PluginConf , ConfException
+ 20
+ 21 from typing import Dict , List , Coroutine , Any
+ 22
+ 23
+ 24 CONF_SCHEMA = { "type" : "object" , "patternProperties" : { ".*" : { "type" : "object" }}}
+ 25
+ 26
+ 27 def _process_conf (
+ 28 message_bus : MessageBus , conf : Dict [ str , PluginConf ]
+ 29 ) -> List [ Coroutine ]:
+ 30 try :
+ 31 conf = fastjsonschema . validate ( CONF_SCHEMA , conf )
+ 32 except fastjsonschema . JsonSchemaException as e :
+ 33 print ( f "Configuration not valid: \n { e } " )
+ 34 return []
+ 35 plugins = PluginRegistry ( "controlpi_plugins" , BasePlugin )
+ 36 coroutines = [ message_bus . run ()]
+ 37 for instance_name in conf :
+ 38 instance_conf = conf [ instance_name ]
+ 39 if "plugin" not in instance_conf :
+ 40 print ( f "No plugin implementation specified for instance ' { instance_name } '." )
+ 41 continue
+ 42 plugin_name = instance_conf [ "plugin" ]
+ 43 if plugin_name not in plugins :
+ 44 print (
+ 45 f "No implementation found for plugin ' { plugin_name } '"
+ 46 f " (specified for instance ' { instance_name } ')."
+ 47 )
+ 48 continue
+ 49 plugin = plugins [ plugin_name ]
+ 50 try :
+ 51 instance = plugin ( message_bus , instance_name , instance_conf )
+ 52 coroutines . append ( instance . run ())
+ 53 except ConfException as e :
+ 54 print ( e )
+ 55 continue
+ 56 return coroutines
+ 57
+ 58
+ 59 async def run ( conf : Dict [ str , PluginConf ]) -> None :
+ 60 """Run the ControlPi system based on a configuration.
+ 61
+ 62 Setup message bus, process given configuration, and run message bus and
+ 63 plugins concurrently and indefinitely.
+ 64
+ 65 This function is mainly used by __main__.py to run a ControlPi system
+ 66 based on a configuration loaded from a configuration JSON file on disk.
+ 67
+ 68 >>> async def test_coroutine():
+ 69 ... conf = {"Example Init":
+ 70 ... {"plugin": "Init",
+ 71 ... "messages": [{"id": 42,
+ 72 ... "content": "Test Message"},
+ 73 ... {"id": 42.42,
+ 74 ... "content": "Second Message"}]},
+ 75 ... "Example Log":
+ 76 ... {"plugin": "Log",
+ 77 ... "filter": [{"sender": {"const": "Example Init"}}]}}
+ 78 ... run_task = asyncio.create_task(run(conf))
+ 79 ... await asyncio.sleep(0.1)
+ 80 ... run_task.cancel()
+ 81 ... try:
+ 82 ... await run_task
+ 83 ... except asyncio.exceptions.CancelledError:
+ 84 ... pass
+ 85 >>> asyncio.run(test_coroutine()) # doctest: +NORMALIZE_WHITESPACE
+ 86 Example Log: {'sender': 'Example Init',
+ 87 'id': 42, 'content': 'Test Message'}
+ 88 Example Log: {'sender': 'Example Init',
+ 89 'id': 42.42, 'content': 'Second Message'}
+ 90 """
+ 91 message_bus = MessageBus ()
+ 92 coroutines = _process_conf ( message_bus , conf )
+ 93 try :
+ 94 await asyncio . gather ( * coroutines )
+ 95 except asyncio . CancelledError :
+ 96 pass
+ 97
+ 98
+ 99 async def test (
+100 conf : Dict [ str , PluginConf ], messages : List [ Dict [ str , Any ]], wait : float = 0.0
+101 ) -> None :
+102 """Test configuration of ControlPi system.
+103
+104 Setup message bus, process given configuration, run message bus and
+105 plugins concurrently, send given messages on message bus and print all
+106 messages on message bus. Terminate when queue of message bus is empty.
+107
+108 This function allows to test single plugins or small plugin
+109 configurations with minimal boilerplate code:
+110 >>> asyncio.run(test(
+111 ... {"Example Init": {"plugin": "Init",
+112 ... "messages": [{"id": 42,
+113 ... "content": "Test Message"},
+114 ... {"id": 42.42,
+115 ... "content": "Second Message"}]}},
+116 ... [{"target": "Example Init",
+117 ... "command": "execute"}])) # doctest: +NORMALIZE_WHITESPACE
+118 test(): {'sender': '', 'event': 'registered',
+119 'client': 'Example Init', 'plugin': 'Init',
+120 'sends': [{'id': {'const': 42},
+121 'content': {'const': 'Test Message'}},
+122 {'id': {'const': 42.42},
+123 'content': {'const': 'Second Message'}}],
+124 'receives': [{'target': {'const': 'Example Init'},
+125 'command': {'const': 'execute'}}]}
+126 test(): {'sender': 'Example Init',
+127 'id': 42, 'content': 'Test Message'}
+128 test(): {'sender': 'Example Init',
+129 'id': 42.42, 'content': 'Second Message'}
+130 test(): {'sender': 'test()', 'target': 'Example Init',
+131 'command': 'execute'}
+132 test(): {'sender': 'Example Init',
+133 'id': 42, 'content': 'Test Message'}
+134 test(): {'sender': 'Example Init',
+135 'id': 42.42, 'content': 'Second Message'}
+136
+137 Similar functionality could be reached by using the Log and Init plugins
+138 to print messages and send some messages on the bus, but these would
+139 clutter the test configuration and code to stop the indefinitely running
+140 bus would have to be added to each and every test.
+141
+142 Incorrect plugin configurations can also be tested by this:
+143 >>> asyncio.run(test(
+144 ... {"Example Init": {"plugin": "Init"}}, []))
+145 data must contain ['messages'] properties
+146 Configuration for 'Example Init' is not valid.
+147 """
+148 message_bus = MessageBus ()
+149
+150 async def log ( message ):
+151 if (
+152 "sender" in message
+153 and message [ "sender" ] == ""
+154 and "event" in message
+155 and message [ "event" ] == "registered"
+156 and "client" in message
+157 and message [ "client" ] == "test()"
+158 ):
+159 # Do not log own registration of 'test()':
+160 return
+161 print ( f "test(): { message } " )
+162
+163 message_bus . register (
+164 "test()" , "Test" , [ MessageTemplate ()], [([ MessageTemplate ()], log )]
+165 )
+166
+167 coroutines = _process_conf ( message_bus , conf )
+168 background_tasks = set ()
+169 for coroutine in coroutines :
+170 task = asyncio . create_task ( coroutine )
+171 background_tasks . add ( task )
+172 task . add_done_callback ( background_tasks . discard )
+173 # Give the created task opportunity to run:
+174 await asyncio . sleep ( 0 )
+175 for message in messages :
+176 await message_bus . send ( Message ( "test()" , message ))
+177 # Give immediate reactions to messages opportunity to happen:
+178 await asyncio . sleep ( 0 )
+179 await asyncio . sleep ( wait )
+180 await message_bus . _queue . join ()
+
+
+
+
+
+
+ CONF_SCHEMA =
+{'type': 'object', 'patternProperties': {'.*': {'type': 'object'}}}
+
+
+
+
+
+
+
+
+
+
+
+
+ async def
+ run (conf : Dict [ str , Dict [ str , Any ]] ) -> None :
+
+ View Source
+
+
+
+ 60 async def run ( conf : Dict [ str , PluginConf ]) -> None :
+61 """Run the ControlPi system based on a configuration.
+62
+63 Setup message bus, process given configuration, and run message bus and
+64 plugins concurrently and indefinitely.
+65
+66 This function is mainly used by __main__.py to run a ControlPi system
+67 based on a configuration loaded from a configuration JSON file on disk.
+68
+69 >>> async def test_coroutine():
+70 ... conf = {"Example Init":
+71 ... {"plugin": "Init",
+72 ... "messages": [{"id": 42,
+73 ... "content": "Test Message"},
+74 ... {"id": 42.42,
+75 ... "content": "Second Message"}]},
+76 ... "Example Log":
+77 ... {"plugin": "Log",
+78 ... "filter": [{"sender": {"const": "Example Init"}}]}}
+79 ... run_task = asyncio.create_task(run(conf))
+80 ... await asyncio.sleep(0.1)
+81 ... run_task.cancel()
+82 ... try:
+83 ... await run_task
+84 ... except asyncio.exceptions.CancelledError:
+85 ... pass
+86 >>> asyncio.run(test_coroutine()) # doctest: +NORMALIZE_WHITESPACE
+87 Example Log: {'sender': 'Example Init',
+88 'id': 42, 'content': 'Test Message'}
+89 Example Log: {'sender': 'Example Init',
+90 'id': 42.42, 'content': 'Second Message'}
+91 """
+92 message_bus = MessageBus ()
+93 coroutines = _process_conf ( message_bus , conf )
+94 try :
+95 await asyncio . gather ( * coroutines )
+96 except asyncio . CancelledError :
+97 pass
+
+
+
+ Run the ControlPi system based on a configuration.
+
+
Setup message bus, process given configuration, and run message bus and
+plugins concurrently and indefinitely.
+
+
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 ()
+... try :
+... await run_task
+... except asyncio . exceptions . CancelledError :
+... pass
+>>> 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'}
+
+
+
+
+
+
+
+
+
+
+ async def
+ test ( conf : Dict [ str , Dict [ str , Any ]] , messages : List [ Dict [ str , Any ]] , wait : float = 0.0 ) -> None :
+
+ View Source
+
+
+
+ 100 async def test (
+101 conf : Dict [ str , PluginConf ], messages : List [ Dict [ str , Any ]], wait : float = 0.0
+102 ) -> None :
+103 """Test configuration of ControlPi system.
+104
+105 Setup message bus, process given configuration, run message bus and
+106 plugins concurrently, send given messages on message bus and print all
+107 messages on message bus. Terminate when queue of message bus is empty.
+108
+109 This function allows to test single plugins or small plugin
+110 configurations with minimal boilerplate code:
+111 >>> asyncio.run(test(
+112 ... {"Example Init": {"plugin": "Init",
+113 ... "messages": [{"id": 42,
+114 ... "content": "Test Message"},
+115 ... {"id": 42.42,
+116 ... "content": "Second Message"}]}},
+117 ... [{"target": "Example Init",
+118 ... "command": "execute"}])) # doctest: +NORMALIZE_WHITESPACE
+119 test(): {'sender': '', 'event': 'registered',
+120 'client': 'Example Init', 'plugin': 'Init',
+121 'sends': [{'id': {'const': 42},
+122 'content': {'const': 'Test Message'}},
+123 {'id': {'const': 42.42},
+124 'content': {'const': 'Second Message'}}],
+125 'receives': [{'target': {'const': 'Example Init'},
+126 'command': {'const': 'execute'}}]}
+127 test(): {'sender': 'Example Init',
+128 'id': 42, 'content': 'Test Message'}
+129 test(): {'sender': 'Example Init',
+130 'id': 42.42, 'content': 'Second Message'}
+131 test(): {'sender': 'test()', 'target': 'Example Init',
+132 'command': 'execute'}
+133 test(): {'sender': 'Example Init',
+134 'id': 42, 'content': 'Test Message'}
+135 test(): {'sender': 'Example Init',
+136 'id': 42.42, 'content': 'Second Message'}
+137
+138 Similar functionality could be reached by using the Log and Init plugins
+139 to print messages and send some messages on the bus, but these would
+140 clutter the test configuration and code to stop the indefinitely running
+141 bus would have to be added to each and every test.
+142
+143 Incorrect plugin configurations can also be tested by this:
+144 >>> asyncio.run(test(
+145 ... {"Example Init": {"plugin": "Init"}}, []))
+146 data must contain ['messages'] properties
+147 Configuration for 'Example Init' is not valid.
+148 """
+149 message_bus = MessageBus ()
+150
+151 async def log ( message ):
+152 if (
+153 "sender" in message
+154 and message [ "sender" ] == ""
+155 and "event" in message
+156 and message [ "event" ] == "registered"
+157 and "client" in message
+158 and message [ "client" ] == "test()"
+159 ):
+160 # Do not log own registration of 'test()':
+161 return
+162 print ( f "test(): { message } " )
+163
+164 message_bus . register (
+165 "test()" , "Test" , [ MessageTemplate ()], [([ MessageTemplate ()], log )]
+166 )
+167
+168 coroutines = _process_conf ( message_bus , conf )
+169 background_tasks = set ()
+170 for coroutine in coroutines :
+171 task = asyncio . create_task ( coroutine )
+172 background_tasks . add ( task )
+173 task . add_done_callback ( background_tasks . discard )
+174 # Give the created task opportunity to run:
+175 await asyncio . sleep ( 0 )
+176 for message in messages :
+177 await message_bus . send ( Message ( "test()" , message ))
+178 # Give immediate reactions to messages opportunity to happen:
+179 await asyncio . sleep ( 0 )
+180 await asyncio . sleep ( wait )
+181 await message_bus . _queue . join ()
+
+
+
+ Test configuration of ControlPi system.
+
+
Setup message bus, process given configuration, run message bus and
+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 ,
+... "content" : "Test Message" },
+... { "id" : 42.42 ,
+... "content" : "Second Message" }]}},
+... [{ "target" : "Example Init" ,
+... "command" : "execute" }])) # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Example Init', 'plugin': 'Init',
+ 'sends': [{'id': {'const': 42},
+ 'content': {'const': 'Test Message'}},
+ {'id': {'const': 42.42},
+ 'content': {'const': 'Second Message'}}],
+ 'receives': [{'target': {'const': 'Example Init'},
+ 'command': {'const': '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 indefinitely 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" }}, []))
+data must contain ['messages'] properties
+Configuration for 'Example Init' is not valid.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/controlpi/baseplugin.html b/doc/api/controlpi/baseplugin.html
new file mode 100644
index 0000000..6089efd
--- /dev/null
+++ b/doc/api/controlpi/baseplugin.html
@@ -0,0 +1,933 @@
+
+
+
+
+
+
+ controlpi.baseplugin API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Define base class for all ControlPi plugins.
+
+
The class BasePlugin provides the abstract base class for concrete plugins
+running on the ControlPi system.
+
+
It has three abstract methods that have to be implemented by all concrete
+plugins:
+
+
+The class property CONF_SCHEMA is the JSON schema of the configuration of
+the plugin. The configuration read from the global configuration file is
+checked against this schema during initialisation.
+The method process_conf is called at the end of initialisation and is used
+to initialise the plugin. It can be assumed that self.bus is the message
+bus of the system, self.name the instance name, and self.conf the
+configuration already validated against the schema.
+The run coroutines of all plugins are executed concurrently by the main
+system.
+
+
+
+
>>> class TestPlugin ( BasePlugin ):
+... CONF_SCHEMA = { 'properties' : { 'key' : { 'type' : 'string' }},
+... 'required' : [ 'key' ]}
+... def process_conf ( self ):
+... if 'key' in self . conf :
+... print ( f "Processing ' { self . conf [ 'key' ] } '." )
+... async def run ( self ):
+... print ( "Doing something else." )
+
+
+
+
Plugins are configured and run based on the information in the global
+configuration. Here, we test this manually:
+
+
+
>>> import asyncio
+>>> async def test ():
+... p = TestPlugin ( MessageBus (), 'Test Instance' , { 'key' : 'Something' })
+... await p . run ()
+>>> asyncio . run ( test ())
+Processing 'Something'.
+Doing something else.
+
+
+
+
Each plugin gets a reference to the system message bus during
+initialisation, which can be accessed as self.bus in the functions of the
+plugin class. This can be used to register and unregister message bus
+clients:
+
+
+
>>> class BusPlugin ( BasePlugin ):
+... CONF_SCHEMA = True
+... def process_conf ( self ):
+... self . bus . register ( self . name , 'BusPlugin' ,
+... [{ 'event' : { 'type' : 'string' }}],
+... [([{ 'target' : { 'const' : self . name }}],
+... self . _receive )])
+... async def _receive ( self , message ):
+... print ( f " { self . name } received { message } ." )
+... await self . bus . send ({ 'sender' : self . name , 'event' : 'Receive' })
+... async def run ( self ):
+... await self . bus . send ({ 'sender' : self . name , 'event' : 'Run' })
+
+
+
+
Again, we run this manually here, but this is done by the main coroutine
+when using the system in production:
+
+
+
>>> async def log ( message ):
+... print ( f "Log: { message } " )
+>>> async def test_bus_plugin ():
+... bus = MessageBus ()
+... p = BusPlugin ( bus , 'Bus Test' , {})
+... bus . register ( 'Test' , 'TestPlugin' ,
+... [{}], [([{ 'sender' : { 'const' : 'Bus Test' }}], log )])
+... bus_task = asyncio . create_task ( bus . run ())
+... plugin_task = asyncio . create_task ( p . run ())
+... await bus . send ({ 'sender' : 'Test' , 'target' : 'Bus Test' , 'key' : 'v' })
+... await asyncio . sleep ( 0 )
+... await asyncio . sleep ( 0 )
+... bus_task . cancel ()
+... try :
+... await asyncio . gather ( bus_task , plugin_task )
+... except asyncio . exceptions . CancelledError :
+... pass
+>>> asyncio . run ( test_bus_plugin ())
+Bus Test received {'sender': 'Test', 'target': 'Bus Test', 'key': 'v'}.
+Log: {'sender': 'Bus Test', 'event': 'Run'}
+Log: {'sender': 'Bus Test', 'event': 'Receive'}
+
+
+
+
Often, there will be a one-to-one correspondence between plugin
+instances and message bus clients, a plugin instance will be a message bus
+client. But there are also cases, where one plugin instance might register
+and unregister a lot of message bus clients, maybe even dynamically through
+its lifetime. A plugin for an input/output card might register separate
+clients for each pin of the card, a plugin for some kind of hardware bus
+might register separate clients for all devices connected to the bus, or a
+network socket plugin might register separate clients for all connections
+to the socket (and unregister them when the connection is closed).
+
+
+
+
+ View Source
+
+ 1 """Define base class for all ControlPi plugins.
+ 2
+ 3 The class BasePlugin provides the abstract base class for concrete plugins
+ 4 running on the ControlPi system.
+ 5
+ 6 It has three abstract methods that have to be implemented by all concrete
+ 7 plugins:
+ 8 - The class property CONF_SCHEMA is the JSON schema of the configuration of
+ 9 the plugin. The configuration read from the global configuration file is
+ 10 checked against this schema during initialisation.
+ 11 - The method process_conf is called at the end of initialisation and is used
+ 12 to initialise the plugin. It can be assumed that self.bus is the message
+ 13 bus of the system, self.name the instance name, and self.conf the
+ 14 configuration already validated against the schema.
+ 15 - The run coroutines of all plugins are executed concurrently by the main
+ 16 system.
+ 17 >>> class TestPlugin(BasePlugin):
+ 18 ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
+ 19 ... 'required': ['key']}
+ 20 ... def process_conf(self):
+ 21 ... if 'key' in self.conf:
+ 22 ... print(f"Processing '{self.conf['key']}'.")
+ 23 ... async def run(self):
+ 24 ... print("Doing something else.")
+ 25
+ 26 Plugins are configured and run based on the information in the global
+ 27 configuration. Here, we test this manually:
+ 28 >>> import asyncio
+ 29 >>> async def test():
+ 30 ... p = TestPlugin(MessageBus(), 'Test Instance', {'key': 'Something'})
+ 31 ... await p.run()
+ 32 >>> asyncio.run(test())
+ 33 Processing 'Something'.
+ 34 Doing something else.
+ 35
+ 36 Each plugin gets a reference to the system message bus during
+ 37 initialisation, which can be accessed as self.bus in the functions of the
+ 38 plugin class. This can be used to register and unregister message bus
+ 39 clients:
+ 40 >>> class BusPlugin(BasePlugin):
+ 41 ... CONF_SCHEMA = True
+ 42 ... def process_conf(self):
+ 43 ... self.bus.register(self.name, 'BusPlugin',
+ 44 ... [{'event': {'type': 'string'}}],
+ 45 ... [([{'target': {'const': self.name}}],
+ 46 ... self._receive)])
+ 47 ... async def _receive(self, message):
+ 48 ... print(f"{self.name} received {message}.")
+ 49 ... await self.bus.send({'sender': self.name, 'event': 'Receive'})
+ 50 ... async def run(self):
+ 51 ... await self.bus.send({'sender': self.name, 'event': 'Run'})
+ 52
+ 53 Again, we run this manually here, but this is done by the main coroutine
+ 54 when using the system in production:
+ 55 >>> async def log(message):
+ 56 ... print(f"Log: {message}")
+ 57 >>> async def test_bus_plugin():
+ 58 ... bus = MessageBus()
+ 59 ... p = BusPlugin(bus, 'Bus Test', {})
+ 60 ... bus.register('Test', 'TestPlugin',
+ 61 ... [{}], [([{'sender': {'const': 'Bus Test'}}], log)])
+ 62 ... bus_task = asyncio.create_task(bus.run())
+ 63 ... plugin_task = asyncio.create_task(p.run())
+ 64 ... await bus.send({'sender': 'Test', 'target': 'Bus Test', 'key': 'v'})
+ 65 ... await asyncio.sleep(0)
+ 66 ... await asyncio.sleep(0)
+ 67 ... bus_task.cancel()
+ 68 ... try:
+ 69 ... await asyncio.gather(bus_task, plugin_task)
+ 70 ... except asyncio.exceptions.CancelledError:
+ 71 ... pass
+ 72 >>> asyncio.run(test_bus_plugin())
+ 73 Bus Test received {'sender': 'Test', 'target': 'Bus Test', 'key': 'v'}.
+ 74 Log: {'sender': 'Bus Test', 'event': 'Run'}
+ 75 Log: {'sender': 'Bus Test', 'event': 'Receive'}
+ 76
+ 77 Often, there will be a one-to-one correspondence between plugin
+ 78 instances and message bus clients, a plugin instance will be a message bus
+ 79 client. But there are also cases, where one plugin instance might register
+ 80 and unregister a lot of message bus clients, maybe even dynamically through
+ 81 its lifetime. A plugin for an input/output card might register separate
+ 82 clients for each pin of the card, a plugin for some kind of hardware bus
+ 83 might register separate clients for all devices connected to the bus, or a
+ 84 network socket plugin might register separate clients for all connections
+ 85 to the socket (and unregister them when the connection is closed).
+ 86 """
+ 87
+ 88 __pdoc__ = { "BasePlugin.CONF_SCHEMA" : False }
+ 89
+ 90 from abc import ABC , abstractmethod
+ 91 import fastjsonschema
+ 92
+ 93 from controlpi.messagebus import MessageBus
+ 94
+ 95 from typing import Union , Dict , List , Any , Optional , Callable
+ 96
+ 97 JSONSchema = Union [
+ 98 bool , Dict [ str , Union [ None , str , int , float , bool , Dict [ str , Any ], List [ Any ]]]
+ 99 ]
+100 # Could be more specific.
+101 PluginConf = Dict [ str , Any ]
+102 # Could be more specific.
+103
+104
+105 class ConfException ( Exception ):
+106 """Raise for errors in plugin configurations."""
+107
+108
+109 class BasePlugin ( ABC ):
+110 """Base class for all ControlPi plugins.
+111
+112 >>> class TestPlugin(BasePlugin):
+113 ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
+114 ... 'required': ['key']}
+115 ... def process_conf(self):
+116 ... if 'key' in self.conf:
+117 ... print(f"Processing '{self.conf['key']}'.")
+118 ... async def run(self):
+119 ... print("Doing something else.")
+120
+121 Initialisation sets the instance variables bus to the given message bus,
+122 name to the given name, and conf to the given configuration:
+123 >>> import asyncio
+124 >>> class TestPlugin(BasePlugin):
+125 ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
+126 ... 'required': ['key']}
+127 ... def process_conf(self):
+128 ... if 'key' in self.conf:
+129 ... print(f"Processing '{self.conf['key']}'.")
+130 ... async def run(self):
+131 ... print("Doing something else.")
+132 >>> async def test():
+133 ... p = TestPlugin(MessageBus(), 'Test Instance',
+134 ... {'key': 'Something'})
+135 ... print(p.bus)
+136 ... print(p.name)
+137 ... print(p.conf)
+138 >>> asyncio.run(test()) # doctest: +ELLIPSIS
+139 Processing 'Something'.
+140 <controlpi.messagebus.MessageBus object at 0x...>
+141 Test Instance
+142 {'key': 'Something'}
+143
+144 It also validates the configuration against the schema in CONF_SCHEMA
+145 and raises ConfException if is not validated.
+146 >>> async def test():
+147 ... p = TestPlugin(MessageBus(), 'Test Instance',
+148 ... {'key': 42})
+149 >>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
+150 Traceback (most recent call last):
+151 ...
+152 baseplugin.ConfException: Configuration for 'Test Instance'
+153 is not valid.
+154 >>> async def test():
+155 ... p = TestPlugin(MessageBus(), 'Test Instance',
+156 ... {'key 2': 'Something'})
+157 >>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
+158 Traceback (most recent call last):
+159 ...
+160 baseplugin.ConfException: Configuration for 'Test Instance'
+161 is not valid.
+162
+163 Finally, it calls process_conf, which is the function that should be
+164 overridden by concrete plugins.
+165 """
+166
+167 CONF_SCHEMA : JSONSchema = False
+168
+169 _validate : Optional [ Callable [[ PluginConf ], PluginConf ]] = None
+170
+171 def __init__ ( self , bus : MessageBus , name : str , conf : PluginConf ) -> None :
+172 # noqa: D107
+173 self . bus = bus
+174 self . name = name
+175 if not type ( self ) . _validate :
+176 type ( self ) . _validate = fastjsonschema . compile ( type ( self ) . CONF_SCHEMA )
+177 self . conf = {}
+178 validate = type ( self ) . _validate
+179 assert validate is not None
+180 try :
+181 self . conf = validate ( conf )
+182 except fastjsonschema . JsonSchemaException as e :
+183 print ( e )
+184 raise ConfException ( f "Configuration for ' { self . name } ' is not valid." )
+185 self . process_conf ()
+186
+187 @abstractmethod
+188 def process_conf ( self ) -> None :
+189 """Process the configuration.
+190
+191 Abstract method has to be overridden by concrete plugins.
+192 process_conf is called at the end of initialisation after the bus
+193 and the configuration are available as self.bus and self.conf, but
+194 before any of the run coroutines are executed.
+195 """
+196 raise NotImplementedError
+197
+198 @abstractmethod
+199 async def run ( self ) -> None :
+200 """Run the plugin.
+201
+202 The coroutine is run concurrently with the message bus and all
+203 other plugins. Initial messages and other tasks can be done here.
+204 It is also okay to run a plugin-specific infinite loop concurrently
+205 with the rest of the system.
+206 """
+207 raise NotImplementedError
+
+
+
+
+
+
+
+ PluginConf =
+typing.Dict[str, typing.Any]
+
+
+
+
+
+
+
+
+
+
+
+
+ class
+ ConfException (builtins.Exception ):
+
+ View Source
+
+
+
+ 106 class ConfException ( Exception ):
+107 """Raise for errors in plugin configurations."""
+
+
+
+ Raise for errors in plugin configurations.
+
+
+
+
+
+
+
+
+ class
+ BasePlugin (abc.ABC ):
+
+ View Source
+
+
+
+ 110 class BasePlugin ( ABC ):
+111 """Base class for all ControlPi plugins.
+112
+113 >>> class TestPlugin(BasePlugin):
+114 ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
+115 ... 'required': ['key']}
+116 ... def process_conf(self):
+117 ... if 'key' in self.conf:
+118 ... print(f"Processing '{self.conf['key']}'.")
+119 ... async def run(self):
+120 ... print("Doing something else.")
+121
+122 Initialisation sets the instance variables bus to the given message bus,
+123 name to the given name, and conf to the given configuration:
+124 >>> import asyncio
+125 >>> class TestPlugin(BasePlugin):
+126 ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
+127 ... 'required': ['key']}
+128 ... def process_conf(self):
+129 ... if 'key' in self.conf:
+130 ... print(f"Processing '{self.conf['key']}'.")
+131 ... async def run(self):
+132 ... print("Doing something else.")
+133 >>> async def test():
+134 ... p = TestPlugin(MessageBus(), 'Test Instance',
+135 ... {'key': 'Something'})
+136 ... print(p.bus)
+137 ... print(p.name)
+138 ... print(p.conf)
+139 >>> asyncio.run(test()) # doctest: +ELLIPSIS
+140 Processing 'Something'.
+141 <controlpi.messagebus.MessageBus object at 0x...>
+142 Test Instance
+143 {'key': 'Something'}
+144
+145 It also validates the configuration against the schema in CONF_SCHEMA
+146 and raises ConfException if is not validated.
+147 >>> async def test():
+148 ... p = TestPlugin(MessageBus(), 'Test Instance',
+149 ... {'key': 42})
+150 >>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
+151 Traceback (most recent call last):
+152 ...
+153 baseplugin.ConfException: Configuration for 'Test Instance'
+154 is not valid.
+155 >>> async def test():
+156 ... p = TestPlugin(MessageBus(), 'Test Instance',
+157 ... {'key 2': 'Something'})
+158 >>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
+159 Traceback (most recent call last):
+160 ...
+161 baseplugin.ConfException: Configuration for 'Test Instance'
+162 is not valid.
+163
+164 Finally, it calls process_conf, which is the function that should be
+165 overridden by concrete plugins.
+166 """
+167
+168 CONF_SCHEMA : JSONSchema = False
+169
+170 _validate : Optional [ Callable [[ PluginConf ], PluginConf ]] = None
+171
+172 def __init__ ( self , bus : MessageBus , name : str , conf : PluginConf ) -> None :
+173 # noqa: D107
+174 self . bus = bus
+175 self . name = name
+176 if not type ( self ) . _validate :
+177 type ( self ) . _validate = fastjsonschema . compile ( type ( self ) . CONF_SCHEMA )
+178 self . conf = {}
+179 validate = type ( self ) . _validate
+180 assert validate is not None
+181 try :
+182 self . conf = validate ( conf )
+183 except fastjsonschema . JsonSchemaException as e :
+184 print ( e )
+185 raise ConfException ( f "Configuration for ' { self . name } ' is not valid." )
+186 self . process_conf ()
+187
+188 @abstractmethod
+189 def process_conf ( self ) -> None :
+190 """Process the configuration.
+191
+192 Abstract method has to be overridden by concrete plugins.
+193 process_conf is called at the end of initialisation after the bus
+194 and the configuration are available as self.bus and self.conf, but
+195 before any of the run coroutines are executed.
+196 """
+197 raise NotImplementedError
+198
+199 @abstractmethod
+200 async def run ( self ) -> None :
+201 """Run the plugin.
+202
+203 The coroutine is run concurrently with the message bus and all
+204 other plugins. Initial messages and other tasks can be done here.
+205 It is also okay to run a plugin-specific infinite loop concurrently
+206 with the rest of the system.
+207 """
+208 raise NotImplementedError
+
+
+
+ Base class for all ControlPi plugins.
+
+
+
>>> class TestPlugin ( BasePlugin ):
+... CONF_SCHEMA = { 'properties' : { 'key' : { 'type' : 'string' }},
+... 'required' : [ 'key' ]}
+... def process_conf ( self ):
+... if 'key' in self . conf :
+... print ( f "Processing ' { self . conf [ 'key' ] } '." )
+... async def run ( self ):
+... print ( "Doing something else." )
+
+
+
+
Initialisation sets the instance variables bus to the given message bus,
+name to the given name, and conf to the given configuration:
+
+
+
>>> import asyncio
+>>> class TestPlugin ( BasePlugin ):
+... CONF_SCHEMA = { 'properties' : { 'key' : { 'type' : 'string' }},
+... 'required' : [ 'key' ]}
+... def process_conf ( self ):
+... if 'key' in self . conf :
+... print ( f "Processing ' { self . conf [ 'key' ] } '." )
+... async def run ( self ):
+... print ( "Doing something else." )
+>>> async def test ():
+... p = TestPlugin ( MessageBus (), 'Test Instance' ,
+... { 'key' : 'Something' })
+... print ( p . bus )
+... print ( p . name )
+... print ( p . conf )
+>>> asyncio . run ( test ()) # doctest: +ELLIPSIS
+Processing 'Something'.
+<controlpi.messagebus.MessageBus object at 0x...>
+Test Instance
+{'key': 'Something'}
+
+
+
+
It also validates the configuration against the schema in CONF_SCHEMA
+and raises ConfException if is not validated.
+
+
+
>>> async def test ():
+... p = TestPlugin ( MessageBus (), 'Test Instance' ,
+... { 'key' : 42 })
+>>> asyncio . run ( test ()) # doctest: +NORMALIZE_WHITESPACE
+Traceback (most recent call last):
+ ...
+baseplugin.ConfException : Configuration for 'Test Instance'
+is not valid.
+>>> async def test ():
+... p = TestPlugin ( MessageBus (), 'Test Instance' ,
+... { 'key 2' : 'Something' })
+>>> asyncio . run ( test ()) # doctest: +NORMALIZE_WHITESPACE
+Traceback (most recent call last):
+ ...
+baseplugin.ConfException : Configuration for 'Test Instance'
+is not valid.
+
+
+
+
Finally, it calls process_conf, which is the function that should be
+overridden by concrete plugins.
+
+
+
+
+
+ CONF_SCHEMA : bool | Dict[str, None | str | int | float | bool | Dict[str, Any] | List[Any]] =
+False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@abstractmethod
+
+
def
+
process_conf (self ) -> None :
+
+
View Source
+
+
+
+
188 @abstractmethod
+189 def process_conf ( self ) -> None :
+190 """Process the configuration.
+191
+192 Abstract method has to be overridden by concrete plugins.
+193 process_conf is called at the end of initialisation after the bus
+194 and the configuration are available as self.bus and self.conf, but
+195 before any of the run coroutines are executed.
+196 """
+197 raise NotImplementedError
+
+
+
+
Process the configuration.
+
+
Abstract method has to be overridden by concrete plugins.
+process_conf is called at the end of initialisation after the bus
+and the configuration are available as self.bus and self.conf, but
+before any of the run coroutines are executed.
+
+
+
+
+
+
+
+
@abstractmethod
+
+
async def
+
run (self ) -> None :
+
+
View Source
+
+
+
+
199 @abstractmethod
+200 async def run ( self ) -> None :
+201 """Run the plugin.
+202
+203 The coroutine is run concurrently with the message bus and all
+204 other plugins. Initial messages and other tasks can be done here.
+205 It is also okay to run a plugin-specific infinite loop concurrently
+206 with the rest of the system.
+207 """
+208 raise NotImplementedError
+
+
+
+
Run the plugin.
+
+
The coroutine is run concurrently with the message bus and all
+other plugins. Initial messages and other tasks can be done here.
+It is also okay to run a plugin-specific infinite loop concurrently
+with the rest of the system.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/controlpi/messagebus.html b/doc/api/controlpi/messagebus.html
new file mode 100644
index 0000000..5ac6739
--- /dev/null
+++ b/doc/api/controlpi/messagebus.html
@@ -0,0 +1,5480 @@
+
+
+
+
+
+
+ controlpi.messagebus API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Provide an asynchronous message bus.
+
+
A message is a dictionary with string keys and string, integer, float,
+Boolean, dictionary, or list values, where the inner dictionaries again
+have string keys and these values and the inner lists also have elements of
+these types. All messages have a special key 'sender' with the name of the
+sending client as string value, which is set by the constructor:
+
+
+
>>> m = Message ( 'Example sender' , { 'key 1' : 'value 1' })
+>>> m [ 'key 2' ] = 'value 2'
+>>> print ( m )
+{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+
+
+
+
A message template is a mapping from string keys to JSON schemas as values.
+A message template matches a message if all keys of the template are
+contained in the message and the values in the message validate against the
+respective schemas. An empty mapping therefore matches all messages.
+
+
The bus executes asynchronous callbacks for all messages to be received by
+a client. We use a simple callback printing the message in all examples:
+
+
+
>>> def callback_for_receiver ( receiver ):
+... async def callback ( message ):
+... print ( f " { receiver } : { message } " )
+... return callback
+
+
+
+
Clients can be registered at the bus with a name, lists of message templates
+they want to use for sending and receiving and a callback function for
+receiving. An empty list of templates means that the client does not want to
+send or receive any messages, respectively. A list with an empty template
+means that it wants to send arbitrary or receive all messages, respectively:
+
+
+
>>> async def setup ( bus ):
+... bus . register ( 'Logger' , 'Test Plugin' ,
+... [],
+... [([ MessageTemplate ({})],
+... callback_for_receiver ( 'Logger' ))])
+... bus . register ( 'Client 1' , 'Test Plugin' ,
+... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})],
+... [([ MessageTemplate ({ 'target' : { 'const' : 'Client 1' }})],
+... callback_for_receiver ( 'Client 1' ))])
+
+
+
+
While most clients should always use their own name for sending, this is not
+enforced and debugging or management clients could send messages on behalf
+of arbitrary client names.
+
+
The name of a client has to be unique and is not allowed to be empty
+(otherwise registration fails).
+
+
The empty name is used to refer to the bus itself. The bus sends messages
+for registrations and deregistrations of clients containing their complete
+interface of send and receive templates. This can be used to allow dynamic
+(debug) clients to deal with arbitrary configurations of clients. The bus
+also reacts to 'get clients' command messages by sending the complete
+information of all currently registered clients.
+
+
Clients can send to the bus with the send function. Each message has to
+declare a sender. The send templates of that sender are checked for a
+template matching the message:
+
+
+
>>> async def send ( bus ):
+... print ( "Sending messages." )
+... await bus . send ({ 'sender' : 'Client 1' , 'k1' : 'Test' })
+... await bus . send ({ 'sender' : '' , 'target' : 'Client 1' })
+
+
+
+
The run function executes the message bus forever. If we want to stop it, we
+have to explicitly cancel the task:
+
+
+
>>> async def main ():
+... bus = MessageBus ()
+... await setup ( bus )
+... bus_task = asyncio . create_task ( bus . run ())
+... await send ( bus )
+... await asyncio . sleep ( 0 )
+... bus_task . cancel ()
+... try :
+... await bus_task
+... except asyncio . exceptions . CancelledError :
+... pass
+>>> asyncio . run ( main ()) # doctest: +NORMALIZE_WHITESPACE
+Sending messages.
+Logger: {'sender': '', 'event': 'registered',
+ 'client': 'Logger', 'plugin': 'Test Plugin',
+ 'sends': [], 'receives': [{}]}
+Logger: {'sender': '', 'event': 'registered',
+ 'client': 'Client 1', 'plugin': 'Test Plugin',
+ 'sends': [{'k1': {'type': 'string'}}],
+ 'receives': [{'target': {'const': 'Client 1'}}]}
+Logger: {'sender': 'Client 1', 'k1': 'Test'}
+Logger: {'sender': '', 'target': 'Client 1'}
+Client 1: {'sender': '', 'target': 'Client 1'}
+
+
+
+
+
+
+ View Source
+
+ 1 """Provide an asynchronous message bus.
+ 2
+ 3 A message is a dictionary with string keys and string, integer, float,
+ 4 Boolean, dictionary, or list values, where the inner dictionaries again
+ 5 have string keys and these values and the inner lists also have elements of
+ 6 these types. All messages have a special key 'sender' with the name of the
+ 7 sending client as string value, which is set by the constructor:
+ 8 >>> m = Message('Example sender', {'key 1': 'value 1'})
+ 9 >>> m['key 2'] = 'value 2'
+ 10 >>> print(m)
+ 11 {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+ 12
+ 13 A message template is a mapping from string keys to JSON schemas as values.
+ 14 A message template matches a message if all keys of the template are
+ 15 contained in the message and the values in the message validate against the
+ 16 respective schemas. An empty mapping therefore matches all messages.
+ 17
+ 18 The bus executes asynchronous callbacks for all messages to be received by
+ 19 a client. We use a simple callback printing the message in all examples:
+ 20 >>> def callback_for_receiver(receiver):
+ 21 ... async def callback(message):
+ 22 ... print(f"{receiver}: {message}")
+ 23 ... return callback
+ 24
+ 25 Clients can be registered at the bus with a name, lists of message templates
+ 26 they want to use for sending and receiving and a callback function for
+ 27 receiving. An empty list of templates means that the client does not want to
+ 28 send or receive any messages, respectively. A list with an empty template
+ 29 means that it wants to send arbitrary or receive all messages, respectively:
+ 30 >>> async def setup(bus):
+ 31 ... bus.register('Logger', 'Test Plugin',
+ 32 ... [],
+ 33 ... [([MessageTemplate({})],
+ 34 ... callback_for_receiver('Logger'))])
+ 35 ... bus.register('Client 1', 'Test Plugin',
+ 36 ... [MessageTemplate({'k1': {'type': 'string'}})],
+ 37 ... [([MessageTemplate({'target': {'const': 'Client 1'}})],
+ 38 ... callback_for_receiver('Client 1'))])
+ 39
+ 40 While most clients should always use their own name for sending, this is not
+ 41 enforced and debugging or management clients could send messages on behalf
+ 42 of arbitrary client names.
+ 43
+ 44 The name of a client has to be unique and is not allowed to be empty
+ 45 (otherwise registration fails).
+ 46
+ 47 The empty name is used to refer to the bus itself. The bus sends messages
+ 48 for registrations and deregistrations of clients containing their complete
+ 49 interface of send and receive templates. This can be used to allow dynamic
+ 50 (debug) clients to deal with arbitrary configurations of clients. The bus
+ 51 also reacts to 'get clients' command messages by sending the complete
+ 52 information of all currently registered clients.
+ 53
+ 54 Clients can send to the bus with the send function. Each message has to
+ 55 declare a sender. The send templates of that sender are checked for a
+ 56 template matching the message:
+ 57 >>> async def send(bus):
+ 58 ... print("Sending messages.")
+ 59 ... await bus.send({'sender': 'Client 1', 'k1': 'Test'})
+ 60 ... await bus.send({'sender': '', 'target': 'Client 1'})
+ 61
+ 62 The run function executes the message bus forever. If we want to stop it, we
+ 63 have to explicitly cancel the task:
+ 64 >>> async def main():
+ 65 ... bus = MessageBus()
+ 66 ... await setup(bus)
+ 67 ... bus_task = asyncio.create_task(bus.run())
+ 68 ... await send(bus)
+ 69 ... await asyncio.sleep(0)
+ 70 ... bus_task.cancel()
+ 71 ... try:
+ 72 ... await bus_task
+ 73 ... except asyncio.exceptions.CancelledError:
+ 74 ... pass
+ 75 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+ 76 Sending messages.
+ 77 Logger: {'sender': '', 'event': 'registered',
+ 78 'client': 'Logger', 'plugin': 'Test Plugin',
+ 79 'sends': [], 'receives': [{}]}
+ 80 Logger: {'sender': '', 'event': 'registered',
+ 81 'client': 'Client 1', 'plugin': 'Test Plugin',
+ 82 'sends': [{'k1': {'type': 'string'}}],
+ 83 'receives': [{'target': {'const': 'Client 1'}}]}
+ 84 Logger: {'sender': 'Client 1', 'k1': 'Test'}
+ 85 Logger: {'sender': '', 'target': 'Client 1'}
+ 86 Client 1: {'sender': '', 'target': 'Client 1'}
+ 87 """
+ 88
+ 89 import asyncio
+ 90 import json
+ 91 import fastjsonschema
+ 92 import sys
+ 93
+ 94 from typing import (
+ 95 Union ,
+ 96 Dict ,
+ 97 List ,
+ 98 Any ,
+ 99 Callable ,
+ 100 Coroutine ,
+ 101 Optional ,
+ 102 Iterable ,
+ 103 Tuple ,
+ 104 )
+ 105
+ 106 MessageValue = Union [ None , str , int , float , bool , Dict [ str , Any ], List [ Any ]]
+ 107 # Should really be:
+ 108 # MessageValue = Union[None, str, int, float, bool,
+ 109 # Dict[str, 'MessageValue'], List['MessageValue']]
+ 110 # But mypy does not support recursion by now:
+ 111 # https://github.com/python/mypy/issues/731
+ 112 JSONSchema = Union [ bool , Dict [ str , MessageValue ]]
+ 113 # Could be even more specific.
+ 114 MessageCallback = Callable [[ "Message" ], Coroutine [ Any , Any , None ]]
+ 115
+ 116
+ 117 # Global cache of JSON schema validation functions:
+ 118 _validates : Dict [ str , Callable [[ MessageValue ], MessageValue ]] = {}
+ 119
+ 120
+ 121 def register_schema ( schema : JSONSchema ) -> bool :
+ 122 """Register the given JSON schema in the global cache."""
+ 123 global _validates
+ 124 schema_string = json . dumps ( schema )
+ 125 if schema_string not in _validates :
+ 126 if not ( isinstance ( schema , dict ) or isinstance ( schema , bool )):
+ 127 return False
+ 128 try :
+ 129 _validates [ schema_string ] = fastjsonschema . compile ( schema )
+ 130 except fastjsonschema . JsonSchemaDefinitionException :
+ 131 return False
+ 132 return True
+ 133
+ 134
+ 135 def validate ( schema_string : str , value : MessageValue ) -> bool :
+ 136 """Validate the given MessageValue against the given JSON schema string."""
+ 137 global _validates
+ 138 if schema_string not in _validates :
+ 139 schema = json . loads ( schema_string )
+ 140 _validates [ schema_string ] = fastjsonschema . compile ( schema )
+ 141 validate = _validates [ schema_string ]
+ 142 try :
+ 143 validate ( value )
+ 144 except fastjsonschema . JsonSchemaException :
+ 145 return False
+ 146 return True
+ 147
+ 148
+ 149 class Message ( Dict [ str , MessageValue ]):
+ 150 """Define arbitrary message.
+ 151
+ 152 Messages are dictionaries with string keys and values that are strings,
+ 153 integers, floats, Booleans, dictionaries that recursively have string
+ 154 keys and values of any of these types, or lists with elements that have
+ 155 any of these types. These constraints are checked when setting key-value
+ 156 pairs of the message.
+ 157
+ 158 A message has to have a sender, which is set by the constructor:
+ 159 >>> m = Message('Example sender')
+ 160 >>> print(m)
+ 161 {'sender': 'Example sender'}
+ 162
+ 163 A dictionary can be given to the constructor:
+ 164 >>> m = Message('Example sender',
+ 165 ... {'key 1': 'value 1', 'key 2': 'value 2'})
+ 166 >>> print(m)
+ 167 {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+ 168
+ 169 A 'sender' set in the initial dictionary is overwritten by the explicitly
+ 170 given sender in the first argument:
+ 171 >>> m = Message('Example sender',
+ 172 ... {'sender': 'Original sender', 'key': 'value'})
+ 173 >>> print(m)
+ 174 {'sender': 'Example sender', 'key': 'value'}
+ 175
+ 176 Or the message can be modified after construction:
+ 177 >>> m = Message('Example sender', {'key 1': 'value 1'})
+ 178 >>> m['key 2'] = 'value 2'
+ 179 >>> print(m)
+ 180 {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+ 181
+ 182 The 'sender' key can be overwritten, but this should only be done in
+ 183 exceptional cases:
+ 184 >>> m = Message('Example sender', {'key': 'value'})
+ 185 >>> m['sender'] = 'New sender'
+ 186 >>> print(m)
+ 187 {'sender': 'New sender', 'key': 'value'}
+ 188 """
+ 189
+ 190 def __init__ (
+ 191 self , sender : str , init : Optional [ Dict [ str , MessageValue ]] = None
+ 192 ) -> None :
+ 193 """Initialise message.
+ 194
+ 195 Message is initialised with given sender and possibly given
+ 196 key-value pairs:
+ 197 >>> m = Message('Example sender')
+ 198 >>> print(m)
+ 199 {'sender': 'Example sender'}
+ 200 >>> m = Message('Example sender', {'key 1': 'value 1'})
+ 201 >>> print(m)
+ 202 {'sender': 'Example sender', 'key 1': 'value 1'}
+ 203 """
+ 204 if not isinstance ( sender , str ):
+ 205 raise TypeError ( f "' { sender } ' is not a valid sender name (not a string)." )
+ 206 self [ "sender" ] = ""
+ 207 if init is not None :
+ 208 self . update ( init )
+ 209 self [ "sender" ] = sender
+ 210
+ 211 @staticmethod
+ 212 def check_value ( value : MessageValue ) -> bool :
+ 213 """Check recursively if a given value is valid.
+ 214
+ 215 None, strings, integers, floats and Booleans are valid:
+ 216 >>> Message.check_value(None)
+ 217 True
+ 218 >>> Message.check_value('Spam')
+ 219 True
+ 220 >>> Message.check_value(42)
+ 221 True
+ 222 >>> Message.check_value(42.42)
+ 223 True
+ 224 >>> Message.check_value(False)
+ 225 True
+ 226
+ 227 Other basic types are not valid:
+ 228 >>> Message.check_value(b'bytes')
+ 229 False
+ 230 >>> Message.check_value(1j)
+ 231 False
+ 232
+ 233 Dictionaries with string keys and recursively valid values are valid:
+ 234 >>> Message.check_value({'str value': 'Spam', 'int value': 42,
+ 235 ... 'float value': 42.42, 'bool value': False})
+ 236 True
+ 237
+ 238 Empty dictionaries are valid:
+ 239 >>> Message.check_value({})
+ 240 True
+ 241
+ 242 Dictionaries with other keys are not valid:
+ 243 >>> Message.check_value({42: 'int key'})
+ 244 False
+ 245
+ 246 Dictionaries with invalid values are not valid:
+ 247 >>> Message.check_value({'complex value': 1j})
+ 248 False
+ 249
+ 250 Lists with valid elements are valid:
+ 251 >>> Message.check_value(['Spam', 42, 42.42, False])
+ 252 True
+ 253
+ 254 Empty lists are valid:
+ 255 >>> Message.check_value([])
+ 256 True
+ 257
+ 258 Lists with invalid elements are not valid:
+ 259 >>> Message.check_value([1j])
+ 260 False
+ 261 """
+ 262 if value is None :
+ 263 return True
+ 264 elif (
+ 265 isinstance ( value , str )
+ 266 or isinstance ( value , int )
+ 267 or isinstance ( value , float )
+ 268 or isinstance ( value , bool )
+ 269 ):
+ 270 return True
+ 271 elif isinstance ( value , dict ):
+ 272 for key in value :
+ 273 if not isinstance ( key , str ):
+ 274 return False
+ 275 if not Message . check_value ( value [ key ]):
+ 276 return False
+ 277 return True
+ 278 elif isinstance ( value , list ):
+ 279 for element in value :
+ 280 if not Message . check_value ( element ):
+ 281 return False
+ 282 return True
+ 283 return False
+ 284
+ 285 def __setitem__ ( self , key : str , value : MessageValue ) -> None :
+ 286 """Check key and value before putting pair into dict.
+ 287
+ 288 >>> m = Message('Example sender')
+ 289 >>> m['key'] = 'value'
+ 290 >>> m['dict'] = {'k1': 'v1', 'k2': 2}
+ 291 >>> print(m) # doctest: +NORMALIZE_WHITESPACE
+ 292 {'sender': 'Example sender', 'key': 'value',
+ 293 'dict': {'k1': 'v1', 'k2': 2}}
+ 294 >>> m = Message('Example sender')
+ 295 >>> m[42] = 'int key'
+ 296 Traceback (most recent call last):
+ 297 ...
+ 298 TypeError: '42' is not a valid key in Message (not a string).
+ 299 >>> m['complex value'] = 1j
+ 300 Traceback (most recent call last):
+ 301 ...
+ 302 TypeError: '1j' is not a valid value in Message.
+ 303 """
+ 304 if not isinstance ( key , str ):
+ 305 raise TypeError ( f "' { key } ' is not a valid key in Message (not a string)." )
+ 306 if not self . check_value ( value ):
+ 307 raise TypeError ( f "' { value } ' is not a valid value in Message." )
+ 308 super () . __setitem__ ( key , value )
+ 309
+ 310 def update ( self , * args , ** kwargs ) -> None :
+ 311 """Override update to use validity checks.
+ 312
+ 313 >>> m = Message('Example sender')
+ 314 >>> m.update({'key 1': 'value 1', 'key 2': 'value 2'})
+ 315 >>> print(m)
+ 316 {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+ 317 >>> m.update({42: 'int key'})
+ 318 Traceback (most recent call last):
+ 319 ...
+ 320 TypeError: '42' is not a valid key in Message (not a string).
+ 321 >>> m.update({'complex value': 1j})
+ 322 Traceback (most recent call last):
+ 323 ...
+ 324 TypeError: '1j' is not a valid value in Message.
+ 325
+ 326 This is also used in __init__:
+ 327 >>> m = Message('Example sender', {'key': 'value'})
+ 328 >>> print(m)
+ 329 {'sender': 'Example sender', 'key': 'value'}
+ 330 >>> m = Message('Example sender', {42: 'int key'})
+ 331 Traceback (most recent call last):
+ 332 ...
+ 333 TypeError: '42' is not a valid key in Message (not a string).
+ 334 >>> m = Message('Example sender', {'complex value': 1j})
+ 335 Traceback (most recent call last):
+ 336 ...
+ 337 TypeError: '1j' is not a valid value in Message.
+ 338 """
+ 339 if args :
+ 340 if len ( args ) > 1 :
+ 341 raise TypeError ( f "update expected at most 1 argument, got { len ( args ) } " )
+ 342 other = dict ( args [ 0 ])
+ 343 for key in other :
+ 344 self [ key ] = other [ key ]
+ 345 for key in kwargs :
+ 346 self [ key ] = kwargs [ key ]
+ 347
+ 348 def setdefault ( self , key : str , value : MessageValue = None ) -> MessageValue :
+ 349 """Override setdefault to use validity checks.
+ 350
+ 351 >>> m = Message('Example sender')
+ 352 >>> m.setdefault('key', 'value 1')
+ 353 'value 1'
+ 354 >>> m.setdefault('key', 'value 2')
+ 355 'value 1'
+ 356 >>> m.setdefault(42, 'int key')
+ 357 Traceback (most recent call last):
+ 358 ...
+ 359 TypeError: '42' is not a valid key in Message (not a string).
+ 360 >>> m.setdefault('complex value', 1j)
+ 361 Traceback (most recent call last):
+ 362 ...
+ 363 TypeError: '1j' is not a valid value in Message.
+ 364
+ 365 But __setitem__ is not called if the key is already present:
+ 366 >>> m.setdefault('key', 1j)
+ 367 'value 1'
+ 368 """
+ 369 if key not in self :
+ 370 self [ key ] = value
+ 371 return self [ key ]
+ 372
+ 373
+ 374 class MessageTemplate ( Dict [ str , JSONSchema ]):
+ 375 """Define a message template.
+ 376
+ 377 A message template is a mapping from string keys to JSON schemas as
+ 378 values:
+ 379 >>> t = MessageTemplate({'key 1': {'const': 'value'},
+ 380 ... 'key 2': {'type': 'string'}})
+ 381 >>> t['key 3'] = {'type': 'object',
+ 382 ... 'properties': {'key 1': {'type': 'number'},
+ 383 ... 'key 2': True}}
+ 384
+ 385 A message template matches a message if all keys of the template are
+ 386 contained in the message and the values in the message validate against
+ 387 the respective schemas:
+ 388 >>> t.check(Message('Example Sender',
+ 389 ... {'key 1': 'value', 'key 2': 'some string',
+ 390 ... 'key 3': {'key 1': 42, 'key 2': None}}))
+ 391 True
+ 392
+ 393 An empty mapping therefore matches all messages:
+ 394 >>> t = MessageTemplate()
+ 395 >>> t.check(Message('Example Sender', {'arbitrary': 'content'}))
+ 396 True
+ 397 """
+ 398
+ 399 def __init__ ( self , init : Optional [ Dict [ str , JSONSchema ]] = None ) -> None :
+ 400 """Initialise message.
+ 401
+ 402 Template is initialised empty or with given key-value pairs:
+ 403 >>> t = MessageTemplate()
+ 404 >>> print(t)
+ 405 {}
+ 406 >>> t = MessageTemplate({'key': {'const': 'value'}})
+ 407 >>> print(t)
+ 408 {'key': {'const': 'value'}}
+ 409 """
+ 410 if init is not None :
+ 411 self . update ( init )
+ 412
+ 413 @staticmethod
+ 414 def from_message ( message : Message ) -> "MessageTemplate" :
+ 415 """Create template from message.
+ 416
+ 417 Template witch constant schemas is created from message:
+ 418 >>> m = Message('Example Sender', {'key': 'value'})
+ 419 >>> t = MessageTemplate.from_message(m)
+ 420 >>> print(t)
+ 421 {'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}}
+ 422 >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
+ 423 ... 'list': [None, True, 'string']})
+ 424 >>> t = MessageTemplate.from_message(m)
+ 425 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+ 426 {'sender': {'const': 'Example Sender'},
+ 427 'dict': {'type': 'object',
+ 428 'properties': {'int': {'const': 42},
+ 429 'float': {'const': 42.42}}},
+ 430 'list': {'type': 'array',
+ 431 'items': [{'const': None},
+ 432 {'const': True},
+ 433 {'const': 'string'}]}}
+ 434
+ 435 This is especially useful for clients that send certain fully
+ 436 predefined messages, where the message is given in the configuration
+ 437 and the template for the registration can be constructed by this
+ 438 method.
+ 439 """
+ 440
+ 441 def schema_from_value ( value : MessageValue ) -> JSONSchema :
+ 442 schema : JSONSchema = False
+ 443 if value is None :
+ 444 schema = { "const" : None }
+ 445 elif (
+ 446 isinstance ( value , str )
+ 447 or isinstance ( value , int )
+ 448 or isinstance ( value , float )
+ 449 or isinstance ( value , bool )
+ 450 ):
+ 451 schema = { "const" : value }
+ 452 elif isinstance ( value , dict ):
+ 453 properties = {}
+ 454 for inner_key in value :
+ 455 inner_value : Message = value [ inner_key ]
+ 456 properties [ inner_key ] = schema_from_value ( inner_value )
+ 457 schema = { "type" : "object" , "properties" : properties }
+ 458 elif isinstance ( value , list ):
+ 459 schema = {
+ 460 "type" : "array" ,
+ 461 "items" : [ schema_from_value ( element ) for element in value ],
+ 462 }
+ 463 return schema
+ 464
+ 465 template = MessageTemplate ()
+ 466 for key in message :
+ 467 template [ key ] = schema_from_value ( message [ key ])
+ 468 return template
+ 469
+ 470 def __setitem__ ( self , key : str , value : JSONSchema ) -> None :
+ 471 """Check key and value before putting pair into dict.
+ 472
+ 473 >>> t = MessageTemplate()
+ 474 >>> t['key 1'] = {'const': 'value'}
+ 475 >>> t['key 2'] = {'type': 'string'}
+ 476 >>> t['key 3'] = {'type': 'object',
+ 477 ... 'properties': {'key 1': {'type': 'number'},
+ 478 ... 'key 2': True}}
+ 479 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+ 480 {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+ 481 'key 3': {'type': 'object',
+ 482 'properties': {'key 1': {'type': 'number'},
+ 483 'key 2': True}}}
+ 484 >>> t[42] = {'const': 'int key'}
+ 485 Traceback (most recent call last):
+ 486 ...
+ 487 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+ 488 >>> t['key'] = 'schema' # doctest: +NORMALIZE_WHITESPACE
+ 489 Traceback (most recent call last):
+ 490 ...
+ 491 TypeError: 'schema' is not a valid value in MessageTemplate
+ 492 (not a valid JSON schema).
+ 493 >>> t['key'] = True
+ 494 """
+ 495 if not isinstance ( key , str ):
+ 496 raise TypeError (
+ 497 f "' { key } ' is not a valid key in MessageTemplate (not a string)."
+ 498 )
+ 499 if not register_schema ( value ):
+ 500 raise TypeError (
+ 501 f "' { value } ' is not a valid value in"
+ 502 " MessageTemplate (not a valid JSON schema)."
+ 503 )
+ 504 super () . __setitem__ ( key , value )
+ 505
+ 506 def update ( self , * args , ** kwargs ) -> None :
+ 507 """Override update to use validity checks.
+ 508
+ 509 >>> t = MessageTemplate()
+ 510 >>> t.update({'key 1': {'const': 'value'},
+ 511 ... 'key 2': {'type': 'string'},
+ 512 ... 'key 3': {'type': 'object',
+ 513 ... 'properties': {'key 1': {'type': 'number'},
+ 514 ... 'key 2': True}}})
+ 515 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+ 516 {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+ 517 'key 3': {'type': 'object',
+ 518 'properties': {'key 1': {'type': 'number'},
+ 519 'key 2': True}}}
+ 520 >>> t.update({42: {'const': 'int key'}})
+ 521 Traceback (most recent call last):
+ 522 ...
+ 523 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+ 524 >>> t.update({'key': 'schema'}) # doctest: +NORMALIZE_WHITESPACE
+ 525 Traceback (most recent call last):
+ 526 ...
+ 527 TypeError: 'schema' is not a valid value in MessageTemplate
+ 528 (not a valid JSON schema).
+ 529 >>> t.update({'key': True})
+ 530
+ 531 This is also used in __init__:
+ 532 >>> t = MessageTemplate({'key 1': {'const': 'value'},
+ 533 ... 'key 2': {'type': 'string'},
+ 534 ... 'key 3': {'type': 'object',
+ 535 ... 'properties': {
+ 536 ... 'key 1': {'type': 'number'},
+ 537 ... 'key 2': True}}})
+ 538 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+ 539 {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+ 540 'key 3': {'type': 'object',
+ 541 'properties': {'key 1': {'type': 'number'},
+ 542 'key 2': True}}}
+ 543 >>> t = MessageTemplate({42: {'const': 'int key'}})
+ 544 Traceback (most recent call last):
+ 545 ...
+ 546 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+ 547 >>> t = MessageTemplate({'key': 'schema'})
+ 548 ... # doctest: +NORMALIZE_WHITESPACE
+ 549 Traceback (most recent call last):
+ 550 ...
+ 551 TypeError: 'schema' is not a valid value in MessageTemplate
+ 552 (not a valid JSON schema).
+ 553 >>> t = MessageTemplate({'key': True})
+ 554 """
+ 555 if args :
+ 556 if len ( args ) > 1 :
+ 557 raise TypeError ( f "update expected at most 1 argument, got { len ( args ) } " )
+ 558 other = dict ( args [ 0 ])
+ 559 for key in other :
+ 560 self [ key ] = other [ key ]
+ 561 for key in kwargs :
+ 562 self [ key ] = kwargs [ key ]
+ 563
+ 564 def setdefault ( self , key : str , value : Optional [ JSONSchema ] = None ) -> JSONSchema :
+ 565 """Override setdefault to use validity checks.
+ 566
+ 567 >>> t = MessageTemplate()
+ 568 >>> t.setdefault('key 1', {'const': 'value'})
+ 569 {'const': 'value'}
+ 570 >>> t.setdefault('key 2', {'type': 'string'})
+ 571 {'type': 'string'}
+ 572 >>> t.setdefault('key 3', {'type': 'object',
+ 573 ... 'properties': {'key 1': {'type': 'number'},
+ 574 ... 'key 2': True}})
+ 575 ... # doctest: +NORMALIZE_WHITESPACE
+ 576 {'type': 'object',
+ 577 'properties': {'key 1': {'type': 'number'},
+ 578 'key 2': True}}
+ 579 >>> t.setdefault(42, {'const': 'int key'})
+ 580 Traceback (most recent call last):
+ 581 ...
+ 582 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+ 583 >>> t.setdefault('key', 'schema') # doctest: +NORMALIZE_WHITESPACE
+ 584 Traceback (most recent call last):
+ 585 ...
+ 586 TypeError: 'schema' is not a valid value in MessageTemplate
+ 587 (not a valid JSON schema).
+ 588
+ 589 But __setitem__ is not called if the key is already present:
+ 590 >>> t.setdefault('key 1', 'schema')
+ 591 {'const': 'value'}
+ 592 """
+ 593 if key not in self :
+ 594 if value is not None :
+ 595 self [ key ] = value
+ 596 else :
+ 597 self [ key ] = True
+ 598 return self [ key ]
+ 599
+ 600 def check ( self , message : Message ) -> bool :
+ 601 """Check message against this template.
+ 602
+ 603 Constant values have to match exactly:
+ 604 >>> t = MessageTemplate({'key': {'const': 'value'}})
+ 605 >>> t.check(Message('Example Sender', {'key': 'value'}))
+ 606 True
+ 607 >>> t.check(Message('Example Sender', {'key': 'other value'}))
+ 608 False
+ 609
+ 610 But for integers, floats with the same value are also valid:
+ 611 >>> t = MessageTemplate({'key': {'const': 42}})
+ 612 >>> t.check(Message('Example Sender', {'key': 42}))
+ 613 True
+ 614 >>> t.check(Message('Example Sender', {'key': 42.0}))
+ 615 True
+ 616
+ 617 Type integer is valid for floats with zero fractional part, but
+ 618 not by floats with non-zero fractional part:
+ 619 >>> t = MessageTemplate({'key': {'type': 'integer'}})
+ 620 >>> t.check(Message('Example Sender', {'key': 42}))
+ 621 True
+ 622 >>> t.check(Message('Example Sender', {'key': 42.0}))
+ 623 True
+ 624 >>> t.check(Message('Example Sender', {'key': 42.42}))
+ 625 False
+ 626
+ 627 Type number is valid for arbitrary ints or floats:
+ 628 >>> t = MessageTemplate({'key': {'type': 'number'}})
+ 629 >>> t.check(Message('Example Sender', {'key': 42}))
+ 630 True
+ 631 >>> t.check(Message('Example Sender', {'key': 42.42}))
+ 632 True
+ 633
+ 634 All keys in template have to be present in message:
+ 635 >>> t = MessageTemplate({'key 1': {'const': 'value'},
+ 636 ... 'key 2': {'type': 'string'},
+ 637 ... 'key 3': {'type': 'object',
+ 638 ... 'properties': {
+ 639 ... 'key 1': {'type': 'number'},
+ 640 ... 'key 2': True,
+ 641 ... 'key 3': False}}})
+ 642 >>> t.check(Message('Example Sender',
+ 643 ... {'key 1': 'value', 'key 2': 'some string'}))
+ 644 False
+ 645
+ 646 But for nested objects their properties do not necessarily have
+ 647 to be present:
+ 648 >>> t.check(Message('Example Sender',
+ 649 ... {'key 1': 'value', 'key 2': 'some string',
+ 650 ... 'key 3': {'key 1': 42}}))
+ 651 True
+ 652
+ 653 Schema True matches everything (even None):
+ 654 >>> t.check(Message('Example Sender',
+ 655 ... {'key 1': 'value', 'key 2': 'some string',
+ 656 ... 'key 3': {'key 2': None}}))
+ 657 True
+ 658
+ 659 Schema False matches nothing:
+ 660 >>> t.check(Message('Example Sender',
+ 661 ... {'key 1': 'value', 'key 2': 'some string',
+ 662 ... 'key 3': {'key 3': True}}))
+ 663 False
+ 664
+ 665 Message is valid for the constant template created from it:
+ 666 >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
+ 667 ... 'list': [None, True, 'string']})
+ 668 >>> t = MessageTemplate.from_message(m)
+ 669 >>> t.check(m)
+ 670 True
+ 671 """
+ 672 for key in self :
+ 673 if key not in message :
+ 674 return False
+ 675 else :
+ 676 schema_string = json . dumps ( self [ key ])
+ 677 if not validate ( schema_string , message [ key ]):
+ 678 return False
+ 679 return True
+ 680
+ 681
+ 682 class TemplateRegistry :
+ 683 """Manage a collection of message templates with registered clients.
+ 684
+ 685 A new TemplateRegistry is created by:
+ 686 >>> r = TemplateRegistry()
+ 687
+ 688 Client names (strings) can be registered for message templates, which
+ 689 are mappings from keys to JSON schemas:
+ 690 >>> r.insert({'k1': {'const': 'v1'}}, 'C 1')
+ 691
+ 692 The check function checks if the templates registered for a client
+ 693 match a given message:
+ 694 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 695 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 696 ... print(f"{m}: {r.check('C 1', m)}")
+ 697 {'k1': 'v1', 'k2': 'v1'}: True
+ 698 {'k1': 'v1', 'k2': 2}: True
+ 699 {'k1': 'v2', 'k2': 'v1'}: False
+ 700 {'k1': 'v2', 'k2': 2}: False
+ 701
+ 702 Clients can be registered for values validating against arbitrary JSON
+ 703 schemas, e.g. all values of a certain type:
+ 704 >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}, 'C 2')
+ 705 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 706 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 707 ... print(f"{m}: {r.check('C 2', m)}")
+ 708 {'k1': 'v1', 'k2': 'v1'}: False
+ 709 {'k1': 'v1', 'k2': 2}: False
+ 710 {'k1': 'v2', 'k2': 'v1'}: True
+ 711 {'k1': 'v2', 'k2': 2}: False
+ 712 >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}, 'C 3')
+ 713 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 714 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 715 ... print(f"{m}: {r.check('C 3', m)}")
+ 716 {'k1': 'v1', 'k2': 'v1'}: False
+ 717 {'k1': 'v1', 'k2': 2}: False
+ 718 {'k1': 'v2', 'k2': 'v1'}: False
+ 719 {'k1': 'v2', 'k2': 2}: True
+ 720
+ 721 The order of key-value pairs does not have to match the order in the
+ 722 messages and keys can be left out:
+ 723 >>> r.insert({'k2': {'const': 2}}, 'C 4')
+ 724 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 725 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 726 ... print(f"{m}: {r.check('C 4', m)}")
+ 727 {'k1': 'v1', 'k2': 'v1'}: False
+ 728 {'k1': 'v1', 'k2': 2}: True
+ 729 {'k1': 'v2', 'k2': 'v1'}: False
+ 730 {'k1': 'v2', 'k2': 2}: True
+ 731
+ 732 A registration for an empty template matches all messages:
+ 733 >>> r.insert({}, 'C 5')
+ 734 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 735 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 736 ... print(f"{m}: {r.check('C 5', m)}")
+ 737 {'k1': 'v1', 'k2': 'v1'}: True
+ 738 {'k1': 'v1', 'k2': 2}: True
+ 739 {'k1': 'v2', 'k2': 'v1'}: True
+ 740 {'k1': 'v2', 'k2': 2}: True
+ 741
+ 742 A client can be registered for multiple templates:
+ 743 >>> r.insert({'k1': {'const': 'v1'}}, 'C 6')
+ 744 >>> r.insert({'k2': {'const': 'v1'}}, 'C 6')
+ 745 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 746 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 747 ... print(f"{m}: {r.check('C 6', m)}")
+ 748 {'k1': 'v1', 'k2': 'v1'}: True
+ 749 {'k1': 'v1', 'k2': 2}: True
+ 750 {'k1': 'v2', 'k2': 'v1'}: True
+ 751 {'k1': 'v2', 'k2': 2}: False
+ 752
+ 753 Clients can be deregistered again (the result is False if the registry
+ 754 is empty after the deletion):
+ 755 >>> r.insert({'k1': {'const': 'v1'}}, 'C 7')
+ 756 >>> r.delete('C 7')
+ 757 True
+ 758 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 759 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 760 ... print(f"{m}: {r.check('C 7', m)}")
+ 761 {'k1': 'v1', 'k2': 'v1'}: False
+ 762 {'k1': 'v1', 'k2': 2}: False
+ 763 {'k1': 'v2', 'k2': 'v1'}: False
+ 764 {'k1': 'v2', 'k2': 2}: False
+ 765
+ 766 The get function returns all clients with registered templates matching
+ 767 a given message:
+ 768 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 769 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 770 ... print(f"{m}: {r.get(m)}")
+ 771 {'k1': 'v1', 'k2': 'v1'}: ['C 5', 'C 1', 'C 6']
+ 772 {'k1': 'v1', 'k2': 2}: ['C 5', 'C 1', 'C 6', 'C 4']
+ 773 {'k1': 'v2', 'k2': 'v1'}: ['C 5', 'C 2', 'C 6']
+ 774 {'k1': 'v2', 'k2': 2}: ['C 5', 'C 3', 'C 4']
+ 775
+ 776 The get_templates function returns all templates for a given client:
+ 777 >>> for c in ['C 1', 'C 2', 'C 3', 'C 4', 'C 5', 'C 6']:
+ 778 ... print(f"{c}: {r.get_templates(c)}")
+ 779 C 1: [{'k1': {'const': 'v1'}}]
+ 780 C 2: [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
+ 781 C 3: [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
+ 782 C 4: [{'k2': {'const': 2}}]
+ 783 C 5: [{}]
+ 784 C 6: [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
+ 785 """
+ 786
+ 787 def __init__ ( self ) -> None :
+ 788 """Initialise an empty registry.
+ 789
+ 790 >>> r = TemplateRegistry()
+ 791 """
+ 792 self . _clients : List [ str ] = []
+ 793 self . _callbacks : Dict [ str , List [ MessageCallback ]] = {}
+ 794 self . _constants : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+ 795 # First key is the message key, second key is the constant string
+ 796 self . _schemas : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+ 797 # First key is the message key, second key is the JSON schema string
+ 798 self . _templates : Dict [ str , List [ MessageTemplate ]] = {}
+ 799
+ 800 def insert (
+ 801 self ,
+ 802 template : MessageTemplate ,
+ 803 client : str ,
+ 804 callback : Optional [ MessageCallback ] = None ,
+ 805 ) -> None :
+ 806 """Register a client for a template.
+ 807
+ 808 >>> r = TemplateRegistry()
+ 809 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'integer'}}, 'C 1')
+ 810 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'string'}}, 'C 2')
+ 811 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v1'}}, 'C 3')
+ 812 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v2'}}, 'C 4')
+ 813 >>> r.insert({}, 'C 5')
+ 814 """
+ 815 if client not in self . _templates :
+ 816 self . _templates [ client ] = []
+ 817 self . _templates [ client ] . append ( template )
+ 818 if not template :
+ 819 self . _clients . append ( client )
+ 820 if callback :
+ 821 if client not in self . _callbacks :
+ 822 self . _callbacks [ client ] = []
+ 823 self . _callbacks [ client ] . append ( callback )
+ 824 else :
+ 825 key , schema = next ( iter ( template . items ()))
+ 826 reduced_template = MessageTemplate (
+ 827 { k : template [ k ] for k in template if k != key }
+ 828 )
+ 829 if (
+ 830 isinstance ( schema , dict )
+ 831 and len ( schema ) == 1
+ 832 and "const" in schema
+ 833 and isinstance ( schema [ "const" ], str )
+ 834 ):
+ 835 value = schema [ "const" ]
+ 836 if key not in self . _constants :
+ 837 self . _constants [ key ] = {}
+ 838 if value not in self . _constants [ key ]:
+ 839 self . _constants [ key ][ value ] = TemplateRegistry ()
+ 840 self . _constants [ key ][ value ] . insert ( reduced_template , client , callback )
+ 841 else :
+ 842 schema_string = json . dumps ( schema )
+ 843 if key not in self . _schemas :
+ 844 self . _schemas [ key ] = {}
+ 845 if schema_string not in self . _schemas [ key ]:
+ 846 self . _schemas [ key ][ schema_string ] = TemplateRegistry ()
+ 847 self . _schemas [ key ][ schema_string ] . insert (
+ 848 reduced_template , client , callback
+ 849 )
+ 850
+ 851 def delete ( self , client : str ) -> bool :
+ 852 """Unregister a client from all templates.
+ 853
+ 854 >>> r = TemplateRegistry()
+ 855 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'integer'}}, 'C 1')
+ 856 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'string'}}, 'C 2')
+ 857 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v1'}}, 'C 3')
+ 858 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v2'}}, 'C 4')
+ 859 >>> r.insert({}, 'C 5')
+ 860 >>> r.delete('C 3')
+ 861 True
+ 862 >>> r.delete('C 4')
+ 863 True
+ 864 """
+ 865 if client in self . _templates :
+ 866 del self . _templates [ client ]
+ 867 self . _clients = [ c for c in self . _clients if c != client ]
+ 868 if client in self . _callbacks :
+ 869 del self . _callbacks [ client ]
+ 870 new_constants : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+ 871 for key in self . _constants :
+ 872 new_constants [ key ] = {}
+ 873 for value in self . _constants [ key ]:
+ 874 if self . _constants [ key ][ value ] . delete ( client ):
+ 875 new_constants [ key ][ value ] = self . _constants [ key ][ value ]
+ 876 if not new_constants [ key ]:
+ 877 del new_constants [ key ]
+ 878 self . _constants = new_constants
+ 879 new_schemas : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+ 880 for key in self . _schemas :
+ 881 new_schemas [ key ] = {}
+ 882 for schema in self . _schemas [ key ]:
+ 883 if self . _schemas [ key ][ schema ] . delete ( client ):
+ 884 new_schemas [ key ][ schema ] = self . _schemas [ key ][ schema ]
+ 885 if not new_schemas [ key ]:
+ 886 del new_schemas [ key ]
+ 887 self . _schemas = new_schemas
+ 888 if self . _clients or self . _callbacks or self . _constants or self . _schemas :
+ 889 return True
+ 890 return False
+ 891
+ 892 def check ( self , client : str , message : Message ) -> bool :
+ 893 """Get if a client has a registered template matching a message.
+ 894
+ 895 >>> r = TemplateRegistry()
+ 896 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+ 897 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 898 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 899 ... print(f"{m}: {r.check('Client 1', m)}")
+ 900 {'k1': 'v1', 'k2': 'v1'}: True
+ 901 {'k1': 'v1', 'k2': 2}: True
+ 902 {'k1': 'v2', 'k2': 'v1'}: False
+ 903 {'k1': 'v2', 'k2': 2}: False
+ 904 >>> r.insert({'k2': {'type': 'integer'}}, 'Client 2')
+ 905 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 906 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 907 ... print(f"{m}: {r.check('Client 2', m)}")
+ 908 {'k1': 'v1', 'k2': 'v1'}: False
+ 909 {'k1': 'v1', 'k2': 2}: True
+ 910 {'k1': 'v2', 'k2': 'v1'}: False
+ 911 {'k1': 'v2', 'k2': 2}: True
+ 912 """
+ 913 if client in self . _clients :
+ 914 return True
+ 915 for key in self . _constants :
+ 916 if (
+ 917 key in message
+ 918 and isinstance ( message [ key ], str )
+ 919 and message [ key ] in self . _constants [ key ]
+ 920 ):
+ 921 value = message [ key ]
+ 922 assert isinstance ( value , str )
+ 923 child = self . _constants [ key ][ value ]
+ 924 if child . check ( client , message ):
+ 925 return True
+ 926 for key in self . _schemas :
+ 927 if key in message :
+ 928 for schema_string in self . _schemas [ key ]:
+ 929 if validate ( schema_string , message [ key ]):
+ 930 child = self . _schemas [ key ][ schema_string ]
+ 931 if child . check ( client , message ):
+ 932 return True
+ 933 return False
+ 934
+ 935 def get ( self , message : Message ) -> List [ str ]:
+ 936 """Get all clients registered for templates matching a message.
+ 937
+ 938 >>> r = TemplateRegistry()
+ 939 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+ 940 >>> r.insert({'k2': {'type': 'integer'}}, 'Client 2')
+ 941 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 942 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 943 ... print(f"{m}: {r.get(m)}")
+ 944 {'k1': 'v1', 'k2': 'v1'}: ['Client 1']
+ 945 {'k1': 'v1', 'k2': 2}: ['Client 1', 'Client 2']
+ 946 {'k1': 'v2', 'k2': 'v1'}: []
+ 947 {'k1': 'v2', 'k2': 2}: ['Client 2']
+ 948 """
+ 949 result = []
+ 950 for client in self . _clients :
+ 951 if client not in result :
+ 952 result . append ( client )
+ 953 for key in self . _constants :
+ 954 if (
+ 955 key in message
+ 956 and isinstance ( message [ key ], str )
+ 957 and message [ key ] in self . _constants [ key ]
+ 958 ):
+ 959 value = message [ key ]
+ 960 assert isinstance ( value , str )
+ 961 child = self . _constants [ key ][ value ]
+ 962 for client in child . get ( message ):
+ 963 if client not in result :
+ 964 result . append ( client )
+ 965 for key in self . _schemas :
+ 966 if key in message :
+ 967 for schema_string in self . _schemas [ key ]:
+ 968 if validate ( schema_string , message [ key ]):
+ 969 child = self . _schemas [ key ][ schema_string ]
+ 970 for client in child . get ( message ):
+ 971 if client not in result :
+ 972 result . append ( client )
+ 973 return result
+ 974
+ 975 def get_callbacks ( self , message : Message ) -> List [ MessageCallback ]:
+ 976 """Get all callbacks registered for templates matching a message."""
+ 977 result = []
+ 978 for client in self . _callbacks :
+ 979 for callback in self . _callbacks [ client ]:
+ 980 if callback not in result :
+ 981 result . append ( callback )
+ 982 for key in self . _constants :
+ 983 if (
+ 984 key in message
+ 985 and isinstance ( message [ key ], str )
+ 986 and message [ key ] in self . _constants [ key ]
+ 987 ):
+ 988 value = message [ key ]
+ 989 assert isinstance ( value , str )
+ 990 child = self . _constants [ key ][ value ]
+ 991 for callback in child . get_callbacks ( message ):
+ 992 if callback not in result :
+ 993 result . append ( callback )
+ 994 for key in self . _schemas :
+ 995 if key in message :
+ 996 for schema_string in self . _schemas [ key ]:
+ 997 if validate ( schema_string , message [ key ]):
+ 998 child = self . _schemas [ key ][ schema_string ]
+ 999 for callback in child . get_callbacks ( message ):
+1000 if callback not in result :
+1001 result . append ( callback )
+1002 return result
+1003
+1004 def get_templates ( self , client : str ) -> List [ MessageTemplate ]:
+1005 """Get all templates for a client.
+1006
+1007 >>> r = TemplateRegistry()
+1008 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+1009 >>> r.get_templates('Client 1')
+1010 [{'k1': {'const': 'v1'}}]
+1011 >>> r.insert({'k1': {'const': 'v2'},
+1012 ... 'k2': {'type': 'string'}}, 'Client 2')
+1013 >>> r.get_templates('Client 2')
+1014 [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
+1015 >>> r.insert({'k1': {'const': 'v2'},
+1016 ... 'k2': {'type': 'integer'}}, 'Client 3')
+1017 >>> r.get_templates('Client 3')
+1018 [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
+1019 >>> r.insert({'k2': {'const': 2}}, 'Client 4')
+1020 >>> r.get_templates('Client 4')
+1021 [{'k2': {'const': 2}}]
+1022 >>> r.insert({}, 'Client 5')
+1023 >>> r.get_templates('Client 5')
+1024 [{}]
+1025 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 6')
+1026 >>> r.insert({'k2': {'const': 'v1'}}, 'Client 6')
+1027 >>> r.get_templates('Client 6')
+1028 [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
+1029 """
+1030 if client in self . _templates :
+1031 return self . _templates [ client ]
+1032 return []
+1033
+1034
+1035 class BusException ( Exception ):
+1036 """Raise for errors in using message bus."""
+1037
+1038
+1039 class MessageBus :
+1040 """Provide an asynchronous message bus.
+1041
+1042 The bus executes asynchronous callbacks for all messages to be received
+1043 by a client. We use a simple callback printing the message in all
+1044 examples:
+1045 >>> def callback_for_receiver(receiver):
+1046 ... print(f"Creating callback for {receiver}.")
+1047 ... async def callback(message):
+1048 ... print(f"{receiver}: {message}")
+1049 ... return callback
+1050
+1051 Clients can be registered at the bus with a name, lists of message
+1052 templates they want to use for sending and receiving and a callback
+1053 function for receiving. An empty list of templates means that the
+1054 client does not want to send or receive any messages, respectively.
+1055 A list with an empty template means that it wants to send arbitrary
+1056 or receive all messages, respectively:
+1057 >>> async def setup(bus):
+1058 ... print("Setting up.")
+1059 ... bus.register('Logger', 'Test Plugin',
+1060 ... [],
+1061 ... [([MessageTemplate({})],
+1062 ... callback_for_receiver('Logger'))])
+1063 ... bus.register('Client 1', 'Test Plugin',
+1064 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1065 ... [([MessageTemplate({'target':
+1066 ... {'const': 'Client 1'}})],
+1067 ... callback_for_receiver('Client 1'))])
+1068 ... bus.register('Client 2', 'Test Plugin',
+1069 ... [MessageTemplate({})],
+1070 ... [([MessageTemplate({'target':
+1071 ... {'const': 'Client 2'}})],
+1072 ... callback_for_receiver('Client 2'))])
+1073
+1074 The bus itself is addressed by the empty string. It sends messages for
+1075 each registration and deregestration of a client with a key 'event' and
+1076 a value of 'registered' or 'unregistered', a key 'client' with the
+1077 client's name as value and for registrations also keys 'sends' and
+1078 'receives' with all templates registered for the client for sending and
+1079 receiving.
+1080
+1081 Clients can send to the bus with the send function. Each message has to
+1082 declare a sender. The send templates of that sender are checked for a
+1083 template matching the message. We cannot prevent arbitrary code from
+1084 impersonating any sender, but this should only be done in debugging or
+1085 management situations.
+1086
+1087 Messages that are intended for a specific client by convention have a
+1088 key 'target' with the target client's name as value. Such messages are
+1089 often commands to the client to do something, which is by convention
+1090 indicated by a key 'command' with a value that indicates what should be
+1091 done.
+1092
+1093 The bus, for example, reacts to a message with 'target': '' and
+1094 'command': 'get clients' by sending one message for each currently
+1095 registered with complete information about its registered send and
+1096 receive templates.
+1097
+1098 >>> async def send(bus):
+1099 ... print("Sending messages.")
+1100 ... await bus.send({'sender': 'Client 1', 'k1': 'Test'})
+1101 ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
+1102 ... await bus.send({'sender': '', 'target': '',
+1103 ... 'command': 'get clients'})
+1104
+1105 The run function executes the message bus forever. If we want to stop
+1106 it, we have to explicitly cancel the task:
+1107 >>> async def main():
+1108 ... bus = MessageBus()
+1109 ... await setup(bus)
+1110 ... bus_task = asyncio.create_task(bus.run())
+1111 ... await send(bus)
+1112 ... await asyncio.sleep(0)
+1113 ... bus_task.cancel()
+1114 ... try:
+1115 ... await bus_task
+1116 ... except asyncio.exceptions.CancelledError:
+1117 ... pass
+1118 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+1119 Setting up.
+1120 Creating callback for Logger.
+1121 Creating callback for Client 1.
+1122 Creating callback for Client 2.
+1123 Sending messages.
+1124 Logger: {'sender': '', 'event': 'registered',
+1125 'client': 'Logger', 'plugin': 'Test Plugin',
+1126 'sends': [], 'receives': [{}]}
+1127 Logger: {'sender': '', 'event': 'registered',
+1128 'client': 'Client 1', 'plugin': 'Test Plugin',
+1129 'sends': [{'k1': {'type': 'string'}}],
+1130 'receives': [{'target': {'const': 'Client 1'}}]}
+1131 Logger: {'sender': '', 'event': 'registered',
+1132 'client': 'Client 2', 'plugin': 'Test Plugin',
+1133 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
+1134 Logger: {'sender': 'Client 1', 'k1': 'Test'}
+1135 Logger: {'sender': 'Client 2', 'target': 'Client 1'}
+1136 Client 1: {'sender': 'Client 2', 'target': 'Client 1'}
+1137 Logger: {'sender': '', 'target': '', 'command': 'get clients'}
+1138 Logger: {'sender': '', 'client': 'Logger', 'plugin': 'Test Plugin',
+1139 'sends': [], 'receives': [{}]}
+1140 Logger: {'sender': '', 'client': 'Client 1', 'plugin': 'Test Plugin',
+1141 'sends': [{'k1': {'type': 'string'}}],
+1142 'receives': [{'target': {'const': 'Client 1'}}]}
+1143 Logger: {'sender': '', 'client': 'Client 2', 'plugin': 'Test Plugin',
+1144 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
+1145 """
+1146
+1147 def __init__ ( self ) -> None :
+1148 """Initialise a new bus without clients.
+1149
+1150 >>> async def main():
+1151 ... bus = MessageBus()
+1152 >>> asyncio.run(main())
+1153 """
+1154 self . _queue : asyncio . Queue = asyncio . Queue ()
+1155 self . _plugins : Dict [ str , str ] = {}
+1156 self . _send_reg : TemplateRegistry = TemplateRegistry ()
+1157 self . _recv_reg : TemplateRegistry = TemplateRegistry ()
+1158
+1159 def register (
+1160 self ,
+1161 client : str ,
+1162 plugin : str ,
+1163 sends : Iterable [ MessageTemplate ],
+1164 receives : Iterable [ Tuple [ Iterable [ MessageTemplate ], MessageCallback ]],
+1165 ) -> None :
+1166 """Register a client at the message bus.
+1167
+1168 >>> async def callback(message):
+1169 ... print(message)
+1170 >>> async def main():
+1171 ... bus = MessageBus()
+1172 ... bus.register('Logger', 'Test Plugin',
+1173 ... [], # send nothing
+1174 ... [([MessageTemplate({})], # receive everything
+1175 ... callback)])
+1176 ... bus.register('Client 1', 'Test Plugin',
+1177 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1178 ... # send with key 'k1' and string value
+1179 ... [([MessageTemplate({'target':
+1180 ... {'const': 'Client 1'}})],
+1181 ... # receive for this client
+1182 ... callback)])
+1183 ... bus.register('Client 2', 'Test Plugin',
+1184 ... [MessageTemplate({})], # send arbitrary
+1185 ... [([MessageTemplate({'target':
+1186 ... {'const': 'Client 2'}})],
+1187 ... # receive for this client
+1188 ... callback)])
+1189 >>> asyncio.run(main())
+1190 """
+1191 if not client :
+1192 raise BusException ( "Client name is not allowed to be empty." )
+1193 if client in self . _plugins :
+1194 raise BusException ( f "Client ' { client } ' already registered at message bus." )
+1195 event = Message ( "" )
+1196 event [ "event" ] = "registered"
+1197 event [ "client" ] = client
+1198 self . _plugins [ client ] = plugin
+1199 event [ "plugin" ] = plugin
+1200 for template in sends :
+1201 self . _send_reg . insert ( template , client )
+1202 event [ "sends" ] = self . _send_reg . get_templates ( client )
+1203 for templates , callback in receives :
+1204 for template in templates :
+1205 self . _recv_reg . insert ( template , client , callback )
+1206 event [ "receives" ] = self . _recv_reg . get_templates ( client )
+1207 self . _queue . put_nowait ( event )
+1208
+1209 def unregister ( self , client : str ) -> None :
+1210 """Unregister a client from the message bus.
+1211
+1212 >>> async def callback(message):
+1213 ... print(message)
+1214 >>> async def main():
+1215 ... bus = MessageBus()
+1216 ... bus.register('Client 1', 'Test Plugin',
+1217 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1218 ... [([MessageTemplate({'target':
+1219 ... {'const': 'Client 1'}})],
+1220 ... callback)])
+1221 ... bus.unregister('Client 1')
+1222 >>> asyncio.run(main())
+1223 """
+1224 if client not in self . _plugins :
+1225 return
+1226 event = Message ( "" )
+1227 event [ "event" ] = "unregistered"
+1228 event [ "client" ] = client
+1229 del self . _plugins [ client ]
+1230 self . _send_reg . delete ( client )
+1231 self . _recv_reg . delete ( client )
+1232 self . _queue . put_nowait ( event )
+1233
+1234 async def run ( self ) -> None :
+1235 """Run the message bus forever.
+1236
+1237 >>> async def main():
+1238 ... bus = MessageBus()
+1239 ... bus_task = asyncio.create_task(bus.run())
+1240 ... bus_task.cancel()
+1241 ... try:
+1242 ... await bus_task
+1243 ... except asyncio.exceptions.CancelledError:
+1244 ... pass
+1245 >>> asyncio.run(main())
+1246 """
+1247 background_tasks = set ()
+1248 while True :
+1249 message = await self . _queue . get ()
+1250 if "target" in message and message [ "target" ] == "" and "command" in message :
+1251 if message [ "command" ] == "get clients" :
+1252 for client in self . _plugins :
+1253 answer = Message ( "" )
+1254 answer [ "client" ] = client
+1255 answer [ "plugin" ] = self . _plugins [ client ]
+1256 answer [ "sends" ] = self . _send_reg . get_templates ( client )
+1257 answer [ "receives" ] = self . _recv_reg . get_templates ( client )
+1258 await self . _queue . put ( answer )
+1259 elif message [ "command" ] == "push conf" :
+1260 conf = {}
+1261 try :
+1262 with open ( sys . argv [ 1 ]) as conf_file :
+1263 conf = json . load ( conf_file )
+1264 except (
+1265 IndexError ,
+1266 FileNotFoundError ,
+1267 json . decoder . JSONDecodeError ,
+1268 ):
+1269 pass
+1270 if conf == message [ "conf" ]:
+1271 await self . _queue . put ( Message ( "" , { "event" : "conf unchanged" }))
+1272 else :
+1273 await self . _queue . put ( Message ( "" , { "event" : "conf changed" }))
+1274 with open ( sys . argv [ 1 ], "w" ) as conf_file :
+1275 json . dump ( message [ "conf" ], conf_file )
+1276 for callback in self . _recv_reg . get_callbacks ( message ):
+1277 task = asyncio . create_task ( callback ( message ))
+1278 background_tasks . add ( task )
+1279 task . add_done_callback ( background_tasks . discard )
+1280 self . _queue . task_done ()
+1281
+1282 async def send ( self , message : Message ) -> None :
+1283 """Send a message to the message bus.
+1284
+1285 >>> async def callback(message):
+1286 ... print(f"Got: {message}")
+1287 >>> async def main():
+1288 ... bus = MessageBus()
+1289 ... bus.register('Client 1', 'Test Plugin',
+1290 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1291 ... [([MessageTemplate({'target':
+1292 ... {'const': 'Client 1'}})],
+1293 ... callback)])
+1294 ... bus.register('Client 2', 'Test Plugin',
+1295 ... [MessageTemplate({})],
+1296 ... [([MessageTemplate({'target':
+1297 ... {'const': 'Client 2'}})],
+1298 ... callback)])
+1299 ... bus_task = asyncio.create_task(bus.run())
+1300 ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
+1301 ... 'k1': 'Test'})
+1302 ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
+1303 ... try:
+1304 ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
+1305 ... 'k1': 42})
+1306 ... except BusException as e:
+1307 ... print(e)
+1308 ... await asyncio.sleep(0)
+1309 ... bus_task.cancel()
+1310 ... try:
+1311 ... await bus_task
+1312 ... except asyncio.exceptions.CancelledError:
+1313 ... pass
+1314 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+1315 Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
+1316 not allowed for sender 'Client 1'.
+1317 Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
+1318 Got: {'sender': 'Client 2', 'target': 'Client 1'}
+1319 """
+1320 assert isinstance ( message [ "sender" ], str )
+1321 sender = message [ "sender" ]
+1322 if sender :
+1323 if not self . _send_reg . check ( sender , message ):
+1324 raise BusException (
+1325 f "Message ' { message } ' not allowed for sender ' { sender } '."
+1326 )
+1327 await self . _queue . put ( message )
+1328
+1329 def send_nowait ( self , message : Message ) -> None :
+1330 """Send a message to the message bus without blocking.
+1331
+1332 >>> async def callback(message):
+1333 ... print(f"Got: {message}")
+1334 >>> async def main():
+1335 ... bus = MessageBus()
+1336 ... bus.register('Client 1', 'Test Plugin',
+1337 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1338 ... [([MessageTemplate({'target':
+1339 ... {'const': 'Client 1'}})],
+1340 ... callback)])
+1341 ... bus.register('Client 2', 'Test Plugin',
+1342 ... [MessageTemplate({})],
+1343 ... [([MessageTemplate({'target':
+1344 ... {'const': 'Client 2'}})],
+1345 ... callback)])
+1346 ... bus_task = asyncio.create_task(bus.run())
+1347 ... bus.send_nowait({'sender': 'Client 1', 'target': 'Client 2',
+1348 ... 'k1': 'Test'})
+1349 ... bus.send_nowait({'sender': 'Client 2', 'target': 'Client 1'})
+1350 ... try:
+1351 ... bus.send_nowait({'sender': 'Client 1',
+1352 ... 'target': 'Client 2', 'k1': 42})
+1353 ... except BusException as e:
+1354 ... print(e)
+1355 ... await asyncio.sleep(0)
+1356 ... bus_task.cancel()
+1357 ... try:
+1358 ... await bus_task
+1359 ... except asyncio.exceptions.CancelledError:
+1360 ... pass
+1361 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+1362 Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
+1363 not allowed for sender 'Client 1'.
+1364 Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
+1365 Got: {'sender': 'Client 2', 'target': 'Client 1'}
+1366 """
+1367 assert isinstance ( message [ "sender" ], str )
+1368 sender = message [ "sender" ]
+1369 if sender :
+1370 if not self . _send_reg . check ( sender , message ):
+1371 raise BusException (
+1372 f "Message ' { message } ' not allowed for sender ' { sender } '."
+1373 )
+1374 self . _queue . put_nowait ( message )
+
+
+
+
+
+
+ MessageValue =
+None | str | int | float | bool | typing.Dict[str, typing.Any] | typing.List[typing.Any]
+
+
+
+
+
+
+
+
+
+
+
+ MessageCallback =
+typing.Callable[[ForwardRef('Message')], typing.Coroutine[typing.Any, typing.Any, NoneType]]
+
+
+
+
+
+
+
+
+
+
+
+
+ def
+ register_schema ( schema : bool | Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]] ) -> bool :
+
+ View Source
+
+
+
+ 122 def register_schema ( schema : JSONSchema ) -> bool :
+123 """Register the given JSON schema in the global cache."""
+124 global _validates
+125 schema_string = json . dumps ( schema )
+126 if schema_string not in _validates :
+127 if not ( isinstance ( schema , dict ) or isinstance ( schema , bool )):
+128 return False
+129 try :
+130 _validates [ schema_string ] = fastjsonschema . compile ( schema )
+131 except fastjsonschema . JsonSchemaDefinitionException :
+132 return False
+133 return True
+
+
+
+ Register the given JSON schema in the global cache.
+
+
+
+
+
+
+
+
+ def
+ validate ( schema_string : str , value : None | str | int | float | bool | Dict [ str , Any ] | List [ Any ] ) -> bool :
+
+ View Source
+
+
+
+ 136 def validate ( schema_string : str , value : MessageValue ) -> bool :
+137 """Validate the given MessageValue against the given JSON schema string."""
+138 global _validates
+139 if schema_string not in _validates :
+140 schema = json . loads ( schema_string )
+141 _validates [ schema_string ] = fastjsonschema . compile ( schema )
+142 validate = _validates [ schema_string ]
+143 try :
+144 validate ( value )
+145 except fastjsonschema . JsonSchemaException :
+146 return False
+147 return True
+
+
+
+ Validate the given MessageValue against the given JSON schema string.
+
+
+
+
+
+
+
+
+ class
+ Message (typing.Dict[str, None | str | int | float | bool | typing.Dict[str, typing.Any] | typing.List[typing.Any]] ):
+
+ View Source
+
+
+
+ 150 class Message ( Dict [ str , MessageValue ]):
+151 """Define arbitrary message.
+152
+153 Messages are dictionaries with string keys and values that are strings,
+154 integers, floats, Booleans, dictionaries that recursively have string
+155 keys and values of any of these types, or lists with elements that have
+156 any of these types. These constraints are checked when setting key-value
+157 pairs of the message.
+158
+159 A message has to have a sender, which is set by the constructor:
+160 >>> m = Message('Example sender')
+161 >>> print(m)
+162 {'sender': 'Example sender'}
+163
+164 A dictionary can be given to the constructor:
+165 >>> m = Message('Example sender',
+166 ... {'key 1': 'value 1', 'key 2': 'value 2'})
+167 >>> print(m)
+168 {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+169
+170 A 'sender' set in the initial dictionary is overwritten by the explicitly
+171 given sender in the first argument:
+172 >>> m = Message('Example sender',
+173 ... {'sender': 'Original sender', 'key': 'value'})
+174 >>> print(m)
+175 {'sender': 'Example sender', 'key': 'value'}
+176
+177 Or the message can be modified after construction:
+178 >>> m = Message('Example sender', {'key 1': 'value 1'})
+179 >>> m['key 2'] = 'value 2'
+180 >>> print(m)
+181 {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+182
+183 The 'sender' key can be overwritten, but this should only be done in
+184 exceptional cases:
+185 >>> m = Message('Example sender', {'key': 'value'})
+186 >>> m['sender'] = 'New sender'
+187 >>> print(m)
+188 {'sender': 'New sender', 'key': 'value'}
+189 """
+190
+191 def __init__ (
+192 self , sender : str , init : Optional [ Dict [ str , MessageValue ]] = None
+193 ) -> None :
+194 """Initialise message.
+195
+196 Message is initialised with given sender and possibly given
+197 key-value pairs:
+198 >>> m = Message('Example sender')
+199 >>> print(m)
+200 {'sender': 'Example sender'}
+201 >>> m = Message('Example sender', {'key 1': 'value 1'})
+202 >>> print(m)
+203 {'sender': 'Example sender', 'key 1': 'value 1'}
+204 """
+205 if not isinstance ( sender , str ):
+206 raise TypeError ( f "' { sender } ' is not a valid sender name (not a string)." )
+207 self [ "sender" ] = ""
+208 if init is not None :
+209 self . update ( init )
+210 self [ "sender" ] = sender
+211
+212 @staticmethod
+213 def check_value ( value : MessageValue ) -> bool :
+214 """Check recursively if a given value is valid.
+215
+216 None, strings, integers, floats and Booleans are valid:
+217 >>> Message.check_value(None)
+218 True
+219 >>> Message.check_value('Spam')
+220 True
+221 >>> Message.check_value(42)
+222 True
+223 >>> Message.check_value(42.42)
+224 True
+225 >>> Message.check_value(False)
+226 True
+227
+228 Other basic types are not valid:
+229 >>> Message.check_value(b'bytes')
+230 False
+231 >>> Message.check_value(1j)
+232 False
+233
+234 Dictionaries with string keys and recursively valid values are valid:
+235 >>> Message.check_value({'str value': 'Spam', 'int value': 42,
+236 ... 'float value': 42.42, 'bool value': False})
+237 True
+238
+239 Empty dictionaries are valid:
+240 >>> Message.check_value({})
+241 True
+242
+243 Dictionaries with other keys are not valid:
+244 >>> Message.check_value({42: 'int key'})
+245 False
+246
+247 Dictionaries with invalid values are not valid:
+248 >>> Message.check_value({'complex value': 1j})
+249 False
+250
+251 Lists with valid elements are valid:
+252 >>> Message.check_value(['Spam', 42, 42.42, False])
+253 True
+254
+255 Empty lists are valid:
+256 >>> Message.check_value([])
+257 True
+258
+259 Lists with invalid elements are not valid:
+260 >>> Message.check_value([1j])
+261 False
+262 """
+263 if value is None :
+264 return True
+265 elif (
+266 isinstance ( value , str )
+267 or isinstance ( value , int )
+268 or isinstance ( value , float )
+269 or isinstance ( value , bool )
+270 ):
+271 return True
+272 elif isinstance ( value , dict ):
+273 for key in value :
+274 if not isinstance ( key , str ):
+275 return False
+276 if not Message . check_value ( value [ key ]):
+277 return False
+278 return True
+279 elif isinstance ( value , list ):
+280 for element in value :
+281 if not Message . check_value ( element ):
+282 return False
+283 return True
+284 return False
+285
+286 def __setitem__ ( self , key : str , value : MessageValue ) -> None :
+287 """Check key and value before putting pair into dict.
+288
+289 >>> m = Message('Example sender')
+290 >>> m['key'] = 'value'
+291 >>> m['dict'] = {'k1': 'v1', 'k2': 2}
+292 >>> print(m) # doctest: +NORMALIZE_WHITESPACE
+293 {'sender': 'Example sender', 'key': 'value',
+294 'dict': {'k1': 'v1', 'k2': 2}}
+295 >>> m = Message('Example sender')
+296 >>> m[42] = 'int key'
+297 Traceback (most recent call last):
+298 ...
+299 TypeError: '42' is not a valid key in Message (not a string).
+300 >>> m['complex value'] = 1j
+301 Traceback (most recent call last):
+302 ...
+303 TypeError: '1j' is not a valid value in Message.
+304 """
+305 if not isinstance ( key , str ):
+306 raise TypeError ( f "' { key } ' is not a valid key in Message (not a string)." )
+307 if not self . check_value ( value ):
+308 raise TypeError ( f "' { value } ' is not a valid value in Message." )
+309 super () . __setitem__ ( key , value )
+310
+311 def update ( self , * args , ** kwargs ) -> None :
+312 """Override update to use validity checks.
+313
+314 >>> m = Message('Example sender')
+315 >>> m.update({'key 1': 'value 1', 'key 2': 'value 2'})
+316 >>> print(m)
+317 {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+318 >>> m.update({42: 'int key'})
+319 Traceback (most recent call last):
+320 ...
+321 TypeError: '42' is not a valid key in Message (not a string).
+322 >>> m.update({'complex value': 1j})
+323 Traceback (most recent call last):
+324 ...
+325 TypeError: '1j' is not a valid value in Message.
+326
+327 This is also used in __init__:
+328 >>> m = Message('Example sender', {'key': 'value'})
+329 >>> print(m)
+330 {'sender': 'Example sender', 'key': 'value'}
+331 >>> m = Message('Example sender', {42: 'int key'})
+332 Traceback (most recent call last):
+333 ...
+334 TypeError: '42' is not a valid key in Message (not a string).
+335 >>> m = Message('Example sender', {'complex value': 1j})
+336 Traceback (most recent call last):
+337 ...
+338 TypeError: '1j' is not a valid value in Message.
+339 """
+340 if args :
+341 if len ( args ) > 1 :
+342 raise TypeError ( f "update expected at most 1 argument, got { len ( args ) } " )
+343 other = dict ( args [ 0 ])
+344 for key in other :
+345 self [ key ] = other [ key ]
+346 for key in kwargs :
+347 self [ key ] = kwargs [ key ]
+348
+349 def setdefault ( self , key : str , value : MessageValue = None ) -> MessageValue :
+350 """Override setdefault to use validity checks.
+351
+352 >>> m = Message('Example sender')
+353 >>> m.setdefault('key', 'value 1')
+354 'value 1'
+355 >>> m.setdefault('key', 'value 2')
+356 'value 1'
+357 >>> m.setdefault(42, 'int key')
+358 Traceback (most recent call last):
+359 ...
+360 TypeError: '42' is not a valid key in Message (not a string).
+361 >>> m.setdefault('complex value', 1j)
+362 Traceback (most recent call last):
+363 ...
+364 TypeError: '1j' is not a valid value in Message.
+365
+366 But __setitem__ is not called if the key is already present:
+367 >>> m.setdefault('key', 1j)
+368 'value 1'
+369 """
+370 if key not in self :
+371 self [ key ] = value
+372 return self [ key ]
+
+
+
+ Define arbitrary message.
+
+
Messages are dictionaries with string keys and values that are strings,
+integers, floats, Booleans, dictionaries that recursively have string
+keys and values of any of these types, or lists with elements that have
+any of these types. These constraints are checked when setting key-value
+pairs of the message.
+
+
A message has to have a sender, which is set by the constructor:
+
+
+
>>> m = Message ( 'Example sender' )
+>>> print ( m )
+{'sender': 'Example sender'}
+
+
+
+
A dictionary can be given to the constructor:
+
+
+
>>> m = Message ( 'Example sender' ,
+... { 'key 1' : 'value 1' , 'key 2' : 'value 2' })
+>>> print ( m )
+{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+
+
+
+
A 'sender' set in the initial dictionary is overwritten by the explicitly
+given sender in the first argument:
+
+
+
>>> m = Message ( 'Example sender' ,
+... { 'sender' : 'Original sender' , 'key' : 'value' })
+>>> print ( m )
+{'sender': 'Example sender', 'key': 'value'}
+
+
+
+
Or the message can be modified after construction:
+
+
+
>>> m = Message ( 'Example sender' , { 'key 1' : 'value 1' })
+>>> m [ 'key 2' ] = 'value 2'
+>>> print ( m )
+{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+
+
+
+
The 'sender' key can be overwritten, but this should only be done in
+exceptional cases:
+
+
+
>>> m = Message ( 'Example sender' , { 'key' : 'value' })
+>>> m [ 'sender' ] = 'New sender'
+>>> print ( m )
+{'sender': 'New sender', 'key': 'value'}
+
+
+
+
+
+
+
+
+
+ Message ( sender : str , init : Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]] | None = None )
+
+ View Source
+
+
+
+
191 def __init__ (
+192 self , sender : str , init : Optional [ Dict [ str , MessageValue ]] = None
+193 ) -> None :
+194 """Initialise message.
+195
+196 Message is initialised with given sender and possibly given
+197 key-value pairs:
+198 >>> m = Message('Example sender')
+199 >>> print(m)
+200 {'sender': 'Example sender'}
+201 >>> m = Message('Example sender', {'key 1': 'value 1'})
+202 >>> print(m)
+203 {'sender': 'Example sender', 'key 1': 'value 1'}
+204 """
+205 if not isinstance ( sender , str ):
+206 raise TypeError ( f "' { sender } ' is not a valid sender name (not a string)." )
+207 self [ "sender" ] = ""
+208 if init is not None :
+209 self . update ( init )
+210 self [ "sender" ] = sender
+
+
+
+
Initialise message.
+
+
Message is initialised with given sender and possibly given
+key-value pairs:
+
+
+
>>> m = Message ( 'Example sender' )
+>>> print ( m )
+{'sender': 'Example sender'}
+>>> m = Message ( 'Example sender' , { 'key 1' : 'value 1' })
+>>> print ( m )
+{'sender': 'Example sender', 'key 1': 'value 1'}
+
+
+
+
+
+
+
+
+
+
@staticmethod
+
+
def
+
check_value ( value : None | str | int | float | bool | Dict [ str , Any ] | List [ Any ] ) -> bool :
+
+
View Source
+
+
+
+
212 @staticmethod
+213 def check_value ( value : MessageValue ) -> bool :
+214 """Check recursively if a given value is valid.
+215
+216 None, strings, integers, floats and Booleans are valid:
+217 >>> Message.check_value(None)
+218 True
+219 >>> Message.check_value('Spam')
+220 True
+221 >>> Message.check_value(42)
+222 True
+223 >>> Message.check_value(42.42)
+224 True
+225 >>> Message.check_value(False)
+226 True
+227
+228 Other basic types are not valid:
+229 >>> Message.check_value(b'bytes')
+230 False
+231 >>> Message.check_value(1j)
+232 False
+233
+234 Dictionaries with string keys and recursively valid values are valid:
+235 >>> Message.check_value({'str value': 'Spam', 'int value': 42,
+236 ... 'float value': 42.42, 'bool value': False})
+237 True
+238
+239 Empty dictionaries are valid:
+240 >>> Message.check_value({})
+241 True
+242
+243 Dictionaries with other keys are not valid:
+244 >>> Message.check_value({42: 'int key'})
+245 False
+246
+247 Dictionaries with invalid values are not valid:
+248 >>> Message.check_value({'complex value': 1j})
+249 False
+250
+251 Lists with valid elements are valid:
+252 >>> Message.check_value(['Spam', 42, 42.42, False])
+253 True
+254
+255 Empty lists are valid:
+256 >>> Message.check_value([])
+257 True
+258
+259 Lists with invalid elements are not valid:
+260 >>> Message.check_value([1j])
+261 False
+262 """
+263 if value is None :
+264 return True
+265 elif (
+266 isinstance ( value , str )
+267 or isinstance ( value , int )
+268 or isinstance ( value , float )
+269 or isinstance ( value , bool )
+270 ):
+271 return True
+272 elif isinstance ( value , dict ):
+273 for key in value :
+274 if not isinstance ( key , str ):
+275 return False
+276 if not Message . check_value ( value [ key ]):
+277 return False
+278 return True
+279 elif isinstance ( value , list ):
+280 for element in value :
+281 if not Message . check_value ( element ):
+282 return False
+283 return True
+284 return False
+
+
+
+
Check recursively if a given value is valid.
+
+
None, strings, integers, floats and Booleans are valid:
+
+
+
+
Other basic types are not valid:
+
+
+
+
Dictionaries with string keys and recursively valid values are valid:
+
+
+
>>> Message.check_value ({ 'str value' : 'Spam' , 'int value' : 42 ,
+... 'float value' : 42.42 , 'bool value' : False })
+True
+
+
+
+
Empty dictionaries are valid:
+
+
+
+
Dictionaries with other keys are not valid:
+
+
+
+
Dictionaries with invalid values are not valid:
+
+
+
+
Lists with valid elements are valid:
+
+
+
+
Empty lists are valid:
+
+
+
+
Lists with invalid elements are not valid:
+
+
+
+
+
+
+
+
+
+
+ def
+ update (self , * args , ** kwargs ) -> None :
+
+ View Source
+
+
+
+
311 def update ( self , * args , ** kwargs ) -> None :
+312 """Override update to use validity checks.
+313
+314 >>> m = Message('Example sender')
+315 >>> m.update({'key 1': 'value 1', 'key 2': 'value 2'})
+316 >>> print(m)
+317 {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+318 >>> m.update({42: 'int key'})
+319 Traceback (most recent call last):
+320 ...
+321 TypeError: '42' is not a valid key in Message (not a string).
+322 >>> m.update({'complex value': 1j})
+323 Traceback (most recent call last):
+324 ...
+325 TypeError: '1j' is not a valid value in Message.
+326
+327 This is also used in __init__:
+328 >>> m = Message('Example sender', {'key': 'value'})
+329 >>> print(m)
+330 {'sender': 'Example sender', 'key': 'value'}
+331 >>> m = Message('Example sender', {42: 'int key'})
+332 Traceback (most recent call last):
+333 ...
+334 TypeError: '42' is not a valid key in Message (not a string).
+335 >>> m = Message('Example sender', {'complex value': 1j})
+336 Traceback (most recent call last):
+337 ...
+338 TypeError: '1j' is not a valid value in Message.
+339 """
+340 if args :
+341 if len ( args ) > 1 :
+342 raise TypeError ( f "update expected at most 1 argument, got { len ( args ) } " )
+343 other = dict ( args [ 0 ])
+344 for key in other :
+345 self [ key ] = other [ key ]
+346 for key in kwargs :
+347 self [ key ] = kwargs [ key ]
+
+
+
+
Override update to use validity checks.
+
+
+
>>> m = Message ( 'Example sender' )
+>>> m . update ({ 'key 1' : 'value 1' , 'key 2' : 'value 2' })
+>>> print ( m )
+{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
+>>> m . update ({ 42 : 'int key' })
+Traceback (most recent call last):
+ ...
+TypeError : '42' is not a valid key in Message (not a string).
+>>> m . update ({ 'complex value' : 1 j })
+Traceback (most recent call last):
+ ...
+TypeError : '1j' is not a valid value in Message.
+
+
+
+
This is also used in __init__:
+
+
+
>>> m = Message ( 'Example sender' , { 'key' : 'value' })
+>>> print ( m )
+{'sender': 'Example sender', 'key': 'value'}
+>>> m = Message ( 'Example sender' , { 42 : 'int key' })
+Traceback (most recent call last):
+ ...
+TypeError : '42' is not a valid key in Message (not a string).
+>>> m = Message ( 'Example sender' , { 'complex value' : 1 j })
+Traceback (most recent call last):
+ ...
+TypeError : '1j' is not a valid value in Message.
+
+
+
+
+
+
+
+
+
+
+ def
+ setdefault ( self , key : str , value : None | str | int | float | bool | Dict [ str , Any ] | List [ Any ] = None ) -> None | str | int | float | bool | Dict [ str , Any ] | List [ Any ] :
+
+ View Source
+
+
+
+
349 def setdefault ( self , key : str , value : MessageValue = None ) -> MessageValue :
+350 """Override setdefault to use validity checks.
+351
+352 >>> m = Message('Example sender')
+353 >>> m.setdefault('key', 'value 1')
+354 'value 1'
+355 >>> m.setdefault('key', 'value 2')
+356 'value 1'
+357 >>> m.setdefault(42, 'int key')
+358 Traceback (most recent call last):
+359 ...
+360 TypeError: '42' is not a valid key in Message (not a string).
+361 >>> m.setdefault('complex value', 1j)
+362 Traceback (most recent call last):
+363 ...
+364 TypeError: '1j' is not a valid value in Message.
+365
+366 But __setitem__ is not called if the key is already present:
+367 >>> m.setdefault('key', 1j)
+368 'value 1'
+369 """
+370 if key not in self :
+371 self [ key ] = value
+372 return self [ key ]
+
+
+
+
Override setdefault to use validity checks.
+
+
+
>>> m = Message ( 'Example sender' )
+>>> m . setdefault ( 'key' , 'value 1' )
+'value 1'
+>>> m . setdefault ( 'key' , 'value 2' )
+'value 1'
+>>> m . setdefault ( 42 , 'int key' )
+Traceback (most recent call last):
+ ...
+TypeError : '42' is not a valid key in Message (not a string).
+>>> m . setdefault ( 'complex value' , 1 j )
+Traceback (most recent call last):
+ ...
+TypeError : '1j' is not a valid value in Message.
+
+
+
+
But __setitem__ is not called if the key is already present:
+
+
+
>>> m . setdefault ( 'key' , 1 j )
+'value 1'
+
+
+
+
+
+
+
+
+
+
+
+ class
+ MessageTemplate (typing.Dict[str, bool | typing.Dict[str, None | str | int | float | bool | typing.Dict[str, typing.Any] | typing.List[typing.Any]]] ):
+
+ View Source
+
+
+
+ 375 class MessageTemplate ( Dict [ str , JSONSchema ]):
+376 """Define a message template.
+377
+378 A message template is a mapping from string keys to JSON schemas as
+379 values:
+380 >>> t = MessageTemplate({'key 1': {'const': 'value'},
+381 ... 'key 2': {'type': 'string'}})
+382 >>> t['key 3'] = {'type': 'object',
+383 ... 'properties': {'key 1': {'type': 'number'},
+384 ... 'key 2': True}}
+385
+386 A message template matches a message if all keys of the template are
+387 contained in the message and the values in the message validate against
+388 the respective schemas:
+389 >>> t.check(Message('Example Sender',
+390 ... {'key 1': 'value', 'key 2': 'some string',
+391 ... 'key 3': {'key 1': 42, 'key 2': None}}))
+392 True
+393
+394 An empty mapping therefore matches all messages:
+395 >>> t = MessageTemplate()
+396 >>> t.check(Message('Example Sender', {'arbitrary': 'content'}))
+397 True
+398 """
+399
+400 def __init__ ( self , init : Optional [ Dict [ str , JSONSchema ]] = None ) -> None :
+401 """Initialise message.
+402
+403 Template is initialised empty or with given key-value pairs:
+404 >>> t = MessageTemplate()
+405 >>> print(t)
+406 {}
+407 >>> t = MessageTemplate({'key': {'const': 'value'}})
+408 >>> print(t)
+409 {'key': {'const': 'value'}}
+410 """
+411 if init is not None :
+412 self . update ( init )
+413
+414 @staticmethod
+415 def from_message ( message : Message ) -> "MessageTemplate" :
+416 """Create template from message.
+417
+418 Template witch constant schemas is created from message:
+419 >>> m = Message('Example Sender', {'key': 'value'})
+420 >>> t = MessageTemplate.from_message(m)
+421 >>> print(t)
+422 {'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}}
+423 >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
+424 ... 'list': [None, True, 'string']})
+425 >>> t = MessageTemplate.from_message(m)
+426 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+427 {'sender': {'const': 'Example Sender'},
+428 'dict': {'type': 'object',
+429 'properties': {'int': {'const': 42},
+430 'float': {'const': 42.42}}},
+431 'list': {'type': 'array',
+432 'items': [{'const': None},
+433 {'const': True},
+434 {'const': 'string'}]}}
+435
+436 This is especially useful for clients that send certain fully
+437 predefined messages, where the message is given in the configuration
+438 and the template for the registration can be constructed by this
+439 method.
+440 """
+441
+442 def schema_from_value ( value : MessageValue ) -> JSONSchema :
+443 schema : JSONSchema = False
+444 if value is None :
+445 schema = { "const" : None }
+446 elif (
+447 isinstance ( value , str )
+448 or isinstance ( value , int )
+449 or isinstance ( value , float )
+450 or isinstance ( value , bool )
+451 ):
+452 schema = { "const" : value }
+453 elif isinstance ( value , dict ):
+454 properties = {}
+455 for inner_key in value :
+456 inner_value : Message = value [ inner_key ]
+457 properties [ inner_key ] = schema_from_value ( inner_value )
+458 schema = { "type" : "object" , "properties" : properties }
+459 elif isinstance ( value , list ):
+460 schema = {
+461 "type" : "array" ,
+462 "items" : [ schema_from_value ( element ) for element in value ],
+463 }
+464 return schema
+465
+466 template = MessageTemplate ()
+467 for key in message :
+468 template [ key ] = schema_from_value ( message [ key ])
+469 return template
+470
+471 def __setitem__ ( self , key : str , value : JSONSchema ) -> None :
+472 """Check key and value before putting pair into dict.
+473
+474 >>> t = MessageTemplate()
+475 >>> t['key 1'] = {'const': 'value'}
+476 >>> t['key 2'] = {'type': 'string'}
+477 >>> t['key 3'] = {'type': 'object',
+478 ... 'properties': {'key 1': {'type': 'number'},
+479 ... 'key 2': True}}
+480 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+481 {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+482 'key 3': {'type': 'object',
+483 'properties': {'key 1': {'type': 'number'},
+484 'key 2': True}}}
+485 >>> t[42] = {'const': 'int key'}
+486 Traceback (most recent call last):
+487 ...
+488 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+489 >>> t['key'] = 'schema' # doctest: +NORMALIZE_WHITESPACE
+490 Traceback (most recent call last):
+491 ...
+492 TypeError: 'schema' is not a valid value in MessageTemplate
+493 (not a valid JSON schema).
+494 >>> t['key'] = True
+495 """
+496 if not isinstance ( key , str ):
+497 raise TypeError (
+498 f "' { key } ' is not a valid key in MessageTemplate (not a string)."
+499 )
+500 if not register_schema ( value ):
+501 raise TypeError (
+502 f "' { value } ' is not a valid value in"
+503 " MessageTemplate (not a valid JSON schema)."
+504 )
+505 super () . __setitem__ ( key , value )
+506
+507 def update ( self , * args , ** kwargs ) -> None :
+508 """Override update to use validity checks.
+509
+510 >>> t = MessageTemplate()
+511 >>> t.update({'key 1': {'const': 'value'},
+512 ... 'key 2': {'type': 'string'},
+513 ... 'key 3': {'type': 'object',
+514 ... 'properties': {'key 1': {'type': 'number'},
+515 ... 'key 2': True}}})
+516 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+517 {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+518 'key 3': {'type': 'object',
+519 'properties': {'key 1': {'type': 'number'},
+520 'key 2': True}}}
+521 >>> t.update({42: {'const': 'int key'}})
+522 Traceback (most recent call last):
+523 ...
+524 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+525 >>> t.update({'key': 'schema'}) # doctest: +NORMALIZE_WHITESPACE
+526 Traceback (most recent call last):
+527 ...
+528 TypeError: 'schema' is not a valid value in MessageTemplate
+529 (not a valid JSON schema).
+530 >>> t.update({'key': True})
+531
+532 This is also used in __init__:
+533 >>> t = MessageTemplate({'key 1': {'const': 'value'},
+534 ... 'key 2': {'type': 'string'},
+535 ... 'key 3': {'type': 'object',
+536 ... 'properties': {
+537 ... 'key 1': {'type': 'number'},
+538 ... 'key 2': True}}})
+539 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+540 {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+541 'key 3': {'type': 'object',
+542 'properties': {'key 1': {'type': 'number'},
+543 'key 2': True}}}
+544 >>> t = MessageTemplate({42: {'const': 'int key'}})
+545 Traceback (most recent call last):
+546 ...
+547 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+548 >>> t = MessageTemplate({'key': 'schema'})
+549 ... # doctest: +NORMALIZE_WHITESPACE
+550 Traceback (most recent call last):
+551 ...
+552 TypeError: 'schema' is not a valid value in MessageTemplate
+553 (not a valid JSON schema).
+554 >>> t = MessageTemplate({'key': True})
+555 """
+556 if args :
+557 if len ( args ) > 1 :
+558 raise TypeError ( f "update expected at most 1 argument, got { len ( args ) } " )
+559 other = dict ( args [ 0 ])
+560 for key in other :
+561 self [ key ] = other [ key ]
+562 for key in kwargs :
+563 self [ key ] = kwargs [ key ]
+564
+565 def setdefault ( self , key : str , value : Optional [ JSONSchema ] = None ) -> JSONSchema :
+566 """Override setdefault to use validity checks.
+567
+568 >>> t = MessageTemplate()
+569 >>> t.setdefault('key 1', {'const': 'value'})
+570 {'const': 'value'}
+571 >>> t.setdefault('key 2', {'type': 'string'})
+572 {'type': 'string'}
+573 >>> t.setdefault('key 3', {'type': 'object',
+574 ... 'properties': {'key 1': {'type': 'number'},
+575 ... 'key 2': True}})
+576 ... # doctest: +NORMALIZE_WHITESPACE
+577 {'type': 'object',
+578 'properties': {'key 1': {'type': 'number'},
+579 'key 2': True}}
+580 >>> t.setdefault(42, {'const': 'int key'})
+581 Traceback (most recent call last):
+582 ...
+583 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+584 >>> t.setdefault('key', 'schema') # doctest: +NORMALIZE_WHITESPACE
+585 Traceback (most recent call last):
+586 ...
+587 TypeError: 'schema' is not a valid value in MessageTemplate
+588 (not a valid JSON schema).
+589
+590 But __setitem__ is not called if the key is already present:
+591 >>> t.setdefault('key 1', 'schema')
+592 {'const': 'value'}
+593 """
+594 if key not in self :
+595 if value is not None :
+596 self [ key ] = value
+597 else :
+598 self [ key ] = True
+599 return self [ key ]
+600
+601 def check ( self , message : Message ) -> bool :
+602 """Check message against this template.
+603
+604 Constant values have to match exactly:
+605 >>> t = MessageTemplate({'key': {'const': 'value'}})
+606 >>> t.check(Message('Example Sender', {'key': 'value'}))
+607 True
+608 >>> t.check(Message('Example Sender', {'key': 'other value'}))
+609 False
+610
+611 But for integers, floats with the same value are also valid:
+612 >>> t = MessageTemplate({'key': {'const': 42}})
+613 >>> t.check(Message('Example Sender', {'key': 42}))
+614 True
+615 >>> t.check(Message('Example Sender', {'key': 42.0}))
+616 True
+617
+618 Type integer is valid for floats with zero fractional part, but
+619 not by floats with non-zero fractional part:
+620 >>> t = MessageTemplate({'key': {'type': 'integer'}})
+621 >>> t.check(Message('Example Sender', {'key': 42}))
+622 True
+623 >>> t.check(Message('Example Sender', {'key': 42.0}))
+624 True
+625 >>> t.check(Message('Example Sender', {'key': 42.42}))
+626 False
+627
+628 Type number is valid for arbitrary ints or floats:
+629 >>> t = MessageTemplate({'key': {'type': 'number'}})
+630 >>> t.check(Message('Example Sender', {'key': 42}))
+631 True
+632 >>> t.check(Message('Example Sender', {'key': 42.42}))
+633 True
+634
+635 All keys in template have to be present in message:
+636 >>> t = MessageTemplate({'key 1': {'const': 'value'},
+637 ... 'key 2': {'type': 'string'},
+638 ... 'key 3': {'type': 'object',
+639 ... 'properties': {
+640 ... 'key 1': {'type': 'number'},
+641 ... 'key 2': True,
+642 ... 'key 3': False}}})
+643 >>> t.check(Message('Example Sender',
+644 ... {'key 1': 'value', 'key 2': 'some string'}))
+645 False
+646
+647 But for nested objects their properties do not necessarily have
+648 to be present:
+649 >>> t.check(Message('Example Sender',
+650 ... {'key 1': 'value', 'key 2': 'some string',
+651 ... 'key 3': {'key 1': 42}}))
+652 True
+653
+654 Schema True matches everything (even None):
+655 >>> t.check(Message('Example Sender',
+656 ... {'key 1': 'value', 'key 2': 'some string',
+657 ... 'key 3': {'key 2': None}}))
+658 True
+659
+660 Schema False matches nothing:
+661 >>> t.check(Message('Example Sender',
+662 ... {'key 1': 'value', 'key 2': 'some string',
+663 ... 'key 3': {'key 3': True}}))
+664 False
+665
+666 Message is valid for the constant template created from it:
+667 >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
+668 ... 'list': [None, True, 'string']})
+669 >>> t = MessageTemplate.from_message(m)
+670 >>> t.check(m)
+671 True
+672 """
+673 for key in self :
+674 if key not in message :
+675 return False
+676 else :
+677 schema_string = json . dumps ( self [ key ])
+678 if not validate ( schema_string , message [ key ]):
+679 return False
+680 return True
+
+
+
+ Define a message template.
+
+
A message template is a mapping from string keys to JSON schemas as
+values:
+
+
+
>>> t = MessageTemplate ({ 'key 1' : { 'const' : 'value' },
+... 'key 2' : { 'type' : 'string' }})
+>>> t [ 'key 3' ] = { 'type' : 'object' ,
+... 'properties' : { 'key 1' : { 'type' : 'number' },
+... 'key 2' : True }}
+
+
+
+
A message template matches a message if all keys of the template are
+contained in the message and the values in the message validate against
+the respective schemas:
+
+
+
>>> t . check ( Message ( 'Example Sender' ,
+... { 'key 1' : 'value' , 'key 2' : 'some string' ,
+... 'key 3' : { 'key 1' : 42 , 'key 2' : None }}))
+True
+
+
+
+
An empty mapping therefore matches all messages:
+
+
+
>>> t = MessageTemplate ()
+>>> t . check ( Message ( 'Example Sender' , { 'arbitrary' : 'content' }))
+True
+
+
+
+
+
+
+
+
+
+ MessageTemplate ( init : Dict [ str , bool | Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]]] | None = None )
+
+ View Source
+
+
+
+
400 def __init__ ( self , init : Optional [ Dict [ str , JSONSchema ]] = None ) -> None :
+401 """Initialise message.
+402
+403 Template is initialised empty or with given key-value pairs:
+404 >>> t = MessageTemplate()
+405 >>> print(t)
+406 {}
+407 >>> t = MessageTemplate({'key': {'const': 'value'}})
+408 >>> print(t)
+409 {'key': {'const': 'value'}}
+410 """
+411 if init is not None :
+412 self . update ( init )
+
+
+
+
Initialise message.
+
+
Template is initialised empty or with given key-value pairs:
+
+
+
>>> t = MessageTemplate ()
+>>> print ( t )
+{}
+>>> t = MessageTemplate ({ 'key' : { 'const' : 'value' }})
+>>> print ( t )
+{'key': {'const': 'value'}}
+
+
+
+
+
+
+
+
+
+
+
414 @staticmethod
+415 def from_message ( message : Message ) -> "MessageTemplate" :
+416 """Create template from message.
+417
+418 Template witch constant schemas is created from message:
+419 >>> m = Message('Example Sender', {'key': 'value'})
+420 >>> t = MessageTemplate.from_message(m)
+421 >>> print(t)
+422 {'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}}
+423 >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
+424 ... 'list': [None, True, 'string']})
+425 >>> t = MessageTemplate.from_message(m)
+426 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+427 {'sender': {'const': 'Example Sender'},
+428 'dict': {'type': 'object',
+429 'properties': {'int': {'const': 42},
+430 'float': {'const': 42.42}}},
+431 'list': {'type': 'array',
+432 'items': [{'const': None},
+433 {'const': True},
+434 {'const': 'string'}]}}
+435
+436 This is especially useful for clients that send certain fully
+437 predefined messages, where the message is given in the configuration
+438 and the template for the registration can be constructed by this
+439 method.
+440 """
+441
+442 def schema_from_value ( value : MessageValue ) -> JSONSchema :
+443 schema : JSONSchema = False
+444 if value is None :
+445 schema = { "const" : None }
+446 elif (
+447 isinstance ( value , str )
+448 or isinstance ( value , int )
+449 or isinstance ( value , float )
+450 or isinstance ( value , bool )
+451 ):
+452 schema = { "const" : value }
+453 elif isinstance ( value , dict ):
+454 properties = {}
+455 for inner_key in value :
+456 inner_value : Message = value [ inner_key ]
+457 properties [ inner_key ] = schema_from_value ( inner_value )
+458 schema = { "type" : "object" , "properties" : properties }
+459 elif isinstance ( value , list ):
+460 schema = {
+461 "type" : "array" ,
+462 "items" : [ schema_from_value ( element ) for element in value ],
+463 }
+464 return schema
+465
+466 template = MessageTemplate ()
+467 for key in message :
+468 template [ key ] = schema_from_value ( message [ key ])
+469 return template
+
+
+
+
Create template from message.
+
+
Template witch constant schemas is created from message:
+
+
+
>>> m = Message ( 'Example Sender' , { 'key' : 'value' })
+>>> t = MessageTemplate.from_message ( m )
+>>> print ( t )
+{'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}}
+>>> m = Message ( 'Example Sender' , { 'dict' : { 'int' : 42 , 'float' : 42.42 },
+... 'list' : [ None , True , 'string' ]})
+>>> t = MessageTemplate.from_message ( m )
+>>> print ( t ) # doctest: +NORMALIZE_WHITESPACE
+{'sender': {'const': 'Example Sender'},
+ 'dict': {'type': 'object',
+ 'properties': {'int': {'const': 42},
+ 'float': {'const': 42.42}}},
+ 'list': {'type': 'array',
+ 'items': [{'const': None},
+ {'const': True},
+ {'const': 'string'}]}}
+
+
+
+
This is especially useful for clients that send certain fully
+predefined messages, where the message is given in the configuration
+and the template for the registration can be constructed by this
+method.
+
+
+
+
+
+
+
+
+ def
+ update (self , * args , ** kwargs ) -> None :
+
+ View Source
+
+
+
+
507 def update ( self , * args , ** kwargs ) -> None :
+508 """Override update to use validity checks.
+509
+510 >>> t = MessageTemplate()
+511 >>> t.update({'key 1': {'const': 'value'},
+512 ... 'key 2': {'type': 'string'},
+513 ... 'key 3': {'type': 'object',
+514 ... 'properties': {'key 1': {'type': 'number'},
+515 ... 'key 2': True}}})
+516 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+517 {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+518 'key 3': {'type': 'object',
+519 'properties': {'key 1': {'type': 'number'},
+520 'key 2': True}}}
+521 >>> t.update({42: {'const': 'int key'}})
+522 Traceback (most recent call last):
+523 ...
+524 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+525 >>> t.update({'key': 'schema'}) # doctest: +NORMALIZE_WHITESPACE
+526 Traceback (most recent call last):
+527 ...
+528 TypeError: 'schema' is not a valid value in MessageTemplate
+529 (not a valid JSON schema).
+530 >>> t.update({'key': True})
+531
+532 This is also used in __init__:
+533 >>> t = MessageTemplate({'key 1': {'const': 'value'},
+534 ... 'key 2': {'type': 'string'},
+535 ... 'key 3': {'type': 'object',
+536 ... 'properties': {
+537 ... 'key 1': {'type': 'number'},
+538 ... 'key 2': True}}})
+539 >>> print(t) # doctest: +NORMALIZE_WHITESPACE
+540 {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+541 'key 3': {'type': 'object',
+542 'properties': {'key 1': {'type': 'number'},
+543 'key 2': True}}}
+544 >>> t = MessageTemplate({42: {'const': 'int key'}})
+545 Traceback (most recent call last):
+546 ...
+547 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+548 >>> t = MessageTemplate({'key': 'schema'})
+549 ... # doctest: +NORMALIZE_WHITESPACE
+550 Traceback (most recent call last):
+551 ...
+552 TypeError: 'schema' is not a valid value in MessageTemplate
+553 (not a valid JSON schema).
+554 >>> t = MessageTemplate({'key': True})
+555 """
+556 if args :
+557 if len ( args ) > 1 :
+558 raise TypeError ( f "update expected at most 1 argument, got { len ( args ) } " )
+559 other = dict ( args [ 0 ])
+560 for key in other :
+561 self [ key ] = other [ key ]
+562 for key in kwargs :
+563 self [ key ] = kwargs [ key ]
+
+
+
+
Override update to use validity checks.
+
+
+
>>> t = MessageTemplate ()
+>>> t . update ({ 'key 1' : { 'const' : 'value' },
+... 'key 2' : { 'type' : 'string' },
+... 'key 3' : { 'type' : 'object' ,
+... 'properties' : { 'key 1' : { 'type' : 'number' },
+... 'key 2' : True }}})
+>>> print ( t ) # doctest: +NORMALIZE_WHITESPACE
+{'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+ 'key 3': {'type': 'object',
+ 'properties': {'key 1': {'type': 'number'},
+ 'key 2': True}}}
+>>> t . update ({ 42 : { 'const' : 'int key' }})
+Traceback (most recent call last):
+ ...
+TypeError : '42' is not a valid key in MessageTemplate (not a string).
+>>> t . update ({ 'key' : 'schema' }) # doctest: +NORMALIZE_WHITESPACE
+Traceback (most recent call last):
+ ...
+TypeError : 'schema' is not a valid value in MessageTemplate
+(not a valid JSON schema).
+>>> t . update ({ 'key' : True })
+
+
+
+
This is also used in __init__:
+
+
+
>>> t = MessageTemplate ({ 'key 1' : { 'const' : 'value' },
+... 'key 2' : { 'type' : 'string' },
+... 'key 3' : { 'type' : 'object' ,
+... 'properties' : {
+... 'key 1' : { 'type' : 'number' },
+... 'key 2' : True }}})
+>>> print ( t ) # doctest: +NORMALIZE_WHITESPACE
+{'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
+ 'key 3': {'type': 'object',
+ 'properties': {'key 1': {'type': 'number'},
+ 'key 2': True}}}
+>>> t = MessageTemplate ({ 42 : { 'const' : 'int key' }})
+Traceback (most recent call last):
+ ...
+TypeError : '42' is not a valid key in MessageTemplate (not a string).
+>>> t = MessageTemplate ({ 'key' : 'schema' })
+... # doctest: +NORMALIZE_WHITESPACE
+Traceback (most recent call last):
+ ...
+TypeError : 'schema' is not a valid value in MessageTemplate
+(not a valid JSON schema).
+>>> t = MessageTemplate ({ 'key' : True })
+
+
+
+
+
+
+
+
+
+
+ def
+ setdefault ( self , key : str , value : bool | Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]] | None = None ) -> bool | Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]] :
+
+ View Source
+
+
+
+
565 def setdefault ( self , key : str , value : Optional [ JSONSchema ] = None ) -> JSONSchema :
+566 """Override setdefault to use validity checks.
+567
+568 >>> t = MessageTemplate()
+569 >>> t.setdefault('key 1', {'const': 'value'})
+570 {'const': 'value'}
+571 >>> t.setdefault('key 2', {'type': 'string'})
+572 {'type': 'string'}
+573 >>> t.setdefault('key 3', {'type': 'object',
+574 ... 'properties': {'key 1': {'type': 'number'},
+575 ... 'key 2': True}})
+576 ... # doctest: +NORMALIZE_WHITESPACE
+577 {'type': 'object',
+578 'properties': {'key 1': {'type': 'number'},
+579 'key 2': True}}
+580 >>> t.setdefault(42, {'const': 'int key'})
+581 Traceback (most recent call last):
+582 ...
+583 TypeError: '42' is not a valid key in MessageTemplate (not a string).
+584 >>> t.setdefault('key', 'schema') # doctest: +NORMALIZE_WHITESPACE
+585 Traceback (most recent call last):
+586 ...
+587 TypeError: 'schema' is not a valid value in MessageTemplate
+588 (not a valid JSON schema).
+589
+590 But __setitem__ is not called if the key is already present:
+591 >>> t.setdefault('key 1', 'schema')
+592 {'const': 'value'}
+593 """
+594 if key not in self :
+595 if value is not None :
+596 self [ key ] = value
+597 else :
+598 self [ key ] = True
+599 return self [ key ]
+
+
+
+
Override setdefault to use validity checks.
+
+
+
>>> t = MessageTemplate ()
+>>> t . setdefault ( 'key 1' , { 'const' : 'value' })
+{'const': 'value'}
+>>> t . setdefault ( 'key 2' , { 'type' : 'string' })
+{'type': 'string'}
+>>> t . setdefault ( 'key 3' , { 'type' : 'object' ,
+... 'properties' : { 'key 1' : { 'type' : 'number' },
+... 'key 2' : True }})
+... # doctest: +NORMALIZE_WHITESPACE
+{'type': 'object',
+ 'properties': {'key 1': {'type': 'number'},
+ 'key 2': True}}
+>>> t . setdefault ( 42 , { 'const' : 'int key' })
+Traceback (most recent call last):
+ ...
+TypeError : '42' is not a valid key in MessageTemplate (not a string).
+>>> t . setdefault ( 'key' , 'schema' ) # doctest: +NORMALIZE_WHITESPACE
+Traceback (most recent call last):
+ ...
+TypeError : 'schema' is not a valid value in MessageTemplate
+(not a valid JSON schema).
+
+
+
+
But __setitem__ is not called if the key is already present:
+
+
+
>>> t . setdefault ( 'key 1' , 'schema' )
+{'const': 'value'}
+
+
+
+
+
+
+
+
+
+
+
def
+
check (self , message : Message ) -> bool :
+
+
View Source
+
+
+
+
601 def check ( self , message : Message ) -> bool :
+602 """Check message against this template.
+603
+604 Constant values have to match exactly:
+605 >>> t = MessageTemplate({'key': {'const': 'value'}})
+606 >>> t.check(Message('Example Sender', {'key': 'value'}))
+607 True
+608 >>> t.check(Message('Example Sender', {'key': 'other value'}))
+609 False
+610
+611 But for integers, floats with the same value are also valid:
+612 >>> t = MessageTemplate({'key': {'const': 42}})
+613 >>> t.check(Message('Example Sender', {'key': 42}))
+614 True
+615 >>> t.check(Message('Example Sender', {'key': 42.0}))
+616 True
+617
+618 Type integer is valid for floats with zero fractional part, but
+619 not by floats with non-zero fractional part:
+620 >>> t = MessageTemplate({'key': {'type': 'integer'}})
+621 >>> t.check(Message('Example Sender', {'key': 42}))
+622 True
+623 >>> t.check(Message('Example Sender', {'key': 42.0}))
+624 True
+625 >>> t.check(Message('Example Sender', {'key': 42.42}))
+626 False
+627
+628 Type number is valid for arbitrary ints or floats:
+629 >>> t = MessageTemplate({'key': {'type': 'number'}})
+630 >>> t.check(Message('Example Sender', {'key': 42}))
+631 True
+632 >>> t.check(Message('Example Sender', {'key': 42.42}))
+633 True
+634
+635 All keys in template have to be present in message:
+636 >>> t = MessageTemplate({'key 1': {'const': 'value'},
+637 ... 'key 2': {'type': 'string'},
+638 ... 'key 3': {'type': 'object',
+639 ... 'properties': {
+640 ... 'key 1': {'type': 'number'},
+641 ... 'key 2': True,
+642 ... 'key 3': False}}})
+643 >>> t.check(Message('Example Sender',
+644 ... {'key 1': 'value', 'key 2': 'some string'}))
+645 False
+646
+647 But for nested objects their properties do not necessarily have
+648 to be present:
+649 >>> t.check(Message('Example Sender',
+650 ... {'key 1': 'value', 'key 2': 'some string',
+651 ... 'key 3': {'key 1': 42}}))
+652 True
+653
+654 Schema True matches everything (even None):
+655 >>> t.check(Message('Example Sender',
+656 ... {'key 1': 'value', 'key 2': 'some string',
+657 ... 'key 3': {'key 2': None}}))
+658 True
+659
+660 Schema False matches nothing:
+661 >>> t.check(Message('Example Sender',
+662 ... {'key 1': 'value', 'key 2': 'some string',
+663 ... 'key 3': {'key 3': True}}))
+664 False
+665
+666 Message is valid for the constant template created from it:
+667 >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
+668 ... 'list': [None, True, 'string']})
+669 >>> t = MessageTemplate.from_message(m)
+670 >>> t.check(m)
+671 True
+672 """
+673 for key in self :
+674 if key not in message :
+675 return False
+676 else :
+677 schema_string = json . dumps ( self [ key ])
+678 if not validate ( schema_string , message [ key ]):
+679 return False
+680 return True
+
+
+
+
Check message against this template.
+
+
Constant values have to match exactly:
+
+
+
>>> t = MessageTemplate ({ 'key' : { 'const' : 'value' }})
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 'value' }))
+True
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 'other value' }))
+False
+
+
+
+
But for integers, floats with the same value are also valid:
+
+
+
>>> t = MessageTemplate ({ 'key' : { 'const' : 42 }})
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 42 }))
+True
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 42.0 }))
+True
+
+
+
+
Type integer is valid for floats with zero fractional part, but
+not by floats with non-zero fractional part:
+
+
+
>>> t = MessageTemplate ({ 'key' : { 'type' : 'integer' }})
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 42 }))
+True
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 42.0 }))
+True
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 42.42 }))
+False
+
+
+
+
Type number is valid for arbitrary ints or floats:
+
+
+
>>> t = MessageTemplate ({ 'key' : { 'type' : 'number' }})
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 42 }))
+True
+>>> t . check ( Message ( 'Example Sender' , { 'key' : 42.42 }))
+True
+
+
+
+
All keys in template have to be present in message:
+
+
+
>>> t = MessageTemplate ({ 'key 1' : { 'const' : 'value' },
+... 'key 2' : { 'type' : 'string' },
+... 'key 3' : { 'type' : 'object' ,
+... 'properties' : {
+... 'key 1' : { 'type' : 'number' },
+... 'key 2' : True ,
+... 'key 3' : False }}})
+>>> t . check ( Message ( 'Example Sender' ,
+... { 'key 1' : 'value' , 'key 2' : 'some string' }))
+False
+
+
+
+
But for nested objects their properties do not necessarily have
+to be present:
+
+
+
>>> t . check ( Message ( 'Example Sender' ,
+... { 'key 1' : 'value' , 'key 2' : 'some string' ,
+... 'key 3' : { 'key 1' : 42 }}))
+True
+
+
+
+
Schema True matches everything (even None):
+
+
+
>>> t . check ( Message ( 'Example Sender' ,
+... { 'key 1' : 'value' , 'key 2' : 'some string' ,
+... 'key 3' : { 'key 2' : None }}))
+True
+
+
+
+
Schema False matches nothing:
+
+
+
>>> t . check ( Message ( 'Example Sender' ,
+... { 'key 1' : 'value' , 'key 2' : 'some string' ,
+... 'key 3' : { 'key 3' : True }}))
+False
+
+
+
+
Message is valid for the constant template created from it:
+
+
+
>>> m = Message ( 'Example Sender' , { 'dict' : { 'int' : 42 , 'float' : 42.42 },
+... 'list' : [ None , True , 'string' ]})
+>>> t = MessageTemplate.from_message ( m )
+>>> t . check ( m )
+True
+
+
+
+
+
+
+
+
+
+
+
+ class
+ TemplateRegistry :
+
+ View Source
+
+
+
+ 683 class TemplateRegistry :
+ 684 """Manage a collection of message templates with registered clients.
+ 685
+ 686 A new TemplateRegistry is created by:
+ 687 >>> r = TemplateRegistry()
+ 688
+ 689 Client names (strings) can be registered for message templates, which
+ 690 are mappings from keys to JSON schemas:
+ 691 >>> r.insert({'k1': {'const': 'v1'}}, 'C 1')
+ 692
+ 693 The check function checks if the templates registered for a client
+ 694 match a given message:
+ 695 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 696 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 697 ... print(f"{m}: {r.check('C 1', m)}")
+ 698 {'k1': 'v1', 'k2': 'v1'}: True
+ 699 {'k1': 'v1', 'k2': 2}: True
+ 700 {'k1': 'v2', 'k2': 'v1'}: False
+ 701 {'k1': 'v2', 'k2': 2}: False
+ 702
+ 703 Clients can be registered for values validating against arbitrary JSON
+ 704 schemas, e.g. all values of a certain type:
+ 705 >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}, 'C 2')
+ 706 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 707 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 708 ... print(f"{m}: {r.check('C 2', m)}")
+ 709 {'k1': 'v1', 'k2': 'v1'}: False
+ 710 {'k1': 'v1', 'k2': 2}: False
+ 711 {'k1': 'v2', 'k2': 'v1'}: True
+ 712 {'k1': 'v2', 'k2': 2}: False
+ 713 >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}, 'C 3')
+ 714 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 715 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 716 ... print(f"{m}: {r.check('C 3', m)}")
+ 717 {'k1': 'v1', 'k2': 'v1'}: False
+ 718 {'k1': 'v1', 'k2': 2}: False
+ 719 {'k1': 'v2', 'k2': 'v1'}: False
+ 720 {'k1': 'v2', 'k2': 2}: True
+ 721
+ 722 The order of key-value pairs does not have to match the order in the
+ 723 messages and keys can be left out:
+ 724 >>> r.insert({'k2': {'const': 2}}, 'C 4')
+ 725 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 726 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 727 ... print(f"{m}: {r.check('C 4', m)}")
+ 728 {'k1': 'v1', 'k2': 'v1'}: False
+ 729 {'k1': 'v1', 'k2': 2}: True
+ 730 {'k1': 'v2', 'k2': 'v1'}: False
+ 731 {'k1': 'v2', 'k2': 2}: True
+ 732
+ 733 A registration for an empty template matches all messages:
+ 734 >>> r.insert({}, 'C 5')
+ 735 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 736 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 737 ... print(f"{m}: {r.check('C 5', m)}")
+ 738 {'k1': 'v1', 'k2': 'v1'}: True
+ 739 {'k1': 'v1', 'k2': 2}: True
+ 740 {'k1': 'v2', 'k2': 'v1'}: True
+ 741 {'k1': 'v2', 'k2': 2}: True
+ 742
+ 743 A client can be registered for multiple templates:
+ 744 >>> r.insert({'k1': {'const': 'v1'}}, 'C 6')
+ 745 >>> r.insert({'k2': {'const': 'v1'}}, 'C 6')
+ 746 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 747 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 748 ... print(f"{m}: {r.check('C 6', m)}")
+ 749 {'k1': 'v1', 'k2': 'v1'}: True
+ 750 {'k1': 'v1', 'k2': 2}: True
+ 751 {'k1': 'v2', 'k2': 'v1'}: True
+ 752 {'k1': 'v2', 'k2': 2}: False
+ 753
+ 754 Clients can be deregistered again (the result is False if the registry
+ 755 is empty after the deletion):
+ 756 >>> r.insert({'k1': {'const': 'v1'}}, 'C 7')
+ 757 >>> r.delete('C 7')
+ 758 True
+ 759 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 760 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 761 ... print(f"{m}: {r.check('C 7', m)}")
+ 762 {'k1': 'v1', 'k2': 'v1'}: False
+ 763 {'k1': 'v1', 'k2': 2}: False
+ 764 {'k1': 'v2', 'k2': 'v1'}: False
+ 765 {'k1': 'v2', 'k2': 2}: False
+ 766
+ 767 The get function returns all clients with registered templates matching
+ 768 a given message:
+ 769 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 770 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 771 ... print(f"{m}: {r.get(m)}")
+ 772 {'k1': 'v1', 'k2': 'v1'}: ['C 5', 'C 1', 'C 6']
+ 773 {'k1': 'v1', 'k2': 2}: ['C 5', 'C 1', 'C 6', 'C 4']
+ 774 {'k1': 'v2', 'k2': 'v1'}: ['C 5', 'C 2', 'C 6']
+ 775 {'k1': 'v2', 'k2': 2}: ['C 5', 'C 3', 'C 4']
+ 776
+ 777 The get_templates function returns all templates for a given client:
+ 778 >>> for c in ['C 1', 'C 2', 'C 3', 'C 4', 'C 5', 'C 6']:
+ 779 ... print(f"{c}: {r.get_templates(c)}")
+ 780 C 1: [{'k1': {'const': 'v1'}}]
+ 781 C 2: [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
+ 782 C 3: [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
+ 783 C 4: [{'k2': {'const': 2}}]
+ 784 C 5: [{}]
+ 785 C 6: [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
+ 786 """
+ 787
+ 788 def __init__ ( self ) -> None :
+ 789 """Initialise an empty registry.
+ 790
+ 791 >>> r = TemplateRegistry()
+ 792 """
+ 793 self . _clients : List [ str ] = []
+ 794 self . _callbacks : Dict [ str , List [ MessageCallback ]] = {}
+ 795 self . _constants : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+ 796 # First key is the message key, second key is the constant string
+ 797 self . _schemas : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+ 798 # First key is the message key, second key is the JSON schema string
+ 799 self . _templates : Dict [ str , List [ MessageTemplate ]] = {}
+ 800
+ 801 def insert (
+ 802 self ,
+ 803 template : MessageTemplate ,
+ 804 client : str ,
+ 805 callback : Optional [ MessageCallback ] = None ,
+ 806 ) -> None :
+ 807 """Register a client for a template.
+ 808
+ 809 >>> r = TemplateRegistry()
+ 810 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'integer'}}, 'C 1')
+ 811 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'string'}}, 'C 2')
+ 812 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v1'}}, 'C 3')
+ 813 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v2'}}, 'C 4')
+ 814 >>> r.insert({}, 'C 5')
+ 815 """
+ 816 if client not in self . _templates :
+ 817 self . _templates [ client ] = []
+ 818 self . _templates [ client ] . append ( template )
+ 819 if not template :
+ 820 self . _clients . append ( client )
+ 821 if callback :
+ 822 if client not in self . _callbacks :
+ 823 self . _callbacks [ client ] = []
+ 824 self . _callbacks [ client ] . append ( callback )
+ 825 else :
+ 826 key , schema = next ( iter ( template . items ()))
+ 827 reduced_template = MessageTemplate (
+ 828 { k : template [ k ] for k in template if k != key }
+ 829 )
+ 830 if (
+ 831 isinstance ( schema , dict )
+ 832 and len ( schema ) == 1
+ 833 and "const" in schema
+ 834 and isinstance ( schema [ "const" ], str )
+ 835 ):
+ 836 value = schema [ "const" ]
+ 837 if key not in self . _constants :
+ 838 self . _constants [ key ] = {}
+ 839 if value not in self . _constants [ key ]:
+ 840 self . _constants [ key ][ value ] = TemplateRegistry ()
+ 841 self . _constants [ key ][ value ] . insert ( reduced_template , client , callback )
+ 842 else :
+ 843 schema_string = json . dumps ( schema )
+ 844 if key not in self . _schemas :
+ 845 self . _schemas [ key ] = {}
+ 846 if schema_string not in self . _schemas [ key ]:
+ 847 self . _schemas [ key ][ schema_string ] = TemplateRegistry ()
+ 848 self . _schemas [ key ][ schema_string ] . insert (
+ 849 reduced_template , client , callback
+ 850 )
+ 851
+ 852 def delete ( self , client : str ) -> bool :
+ 853 """Unregister a client from all templates.
+ 854
+ 855 >>> r = TemplateRegistry()
+ 856 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'integer'}}, 'C 1')
+ 857 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'string'}}, 'C 2')
+ 858 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v1'}}, 'C 3')
+ 859 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v2'}}, 'C 4')
+ 860 >>> r.insert({}, 'C 5')
+ 861 >>> r.delete('C 3')
+ 862 True
+ 863 >>> r.delete('C 4')
+ 864 True
+ 865 """
+ 866 if client in self . _templates :
+ 867 del self . _templates [ client ]
+ 868 self . _clients = [ c for c in self . _clients if c != client ]
+ 869 if client in self . _callbacks :
+ 870 del self . _callbacks [ client ]
+ 871 new_constants : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+ 872 for key in self . _constants :
+ 873 new_constants [ key ] = {}
+ 874 for value in self . _constants [ key ]:
+ 875 if self . _constants [ key ][ value ] . delete ( client ):
+ 876 new_constants [ key ][ value ] = self . _constants [ key ][ value ]
+ 877 if not new_constants [ key ]:
+ 878 del new_constants [ key ]
+ 879 self . _constants = new_constants
+ 880 new_schemas : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+ 881 for key in self . _schemas :
+ 882 new_schemas [ key ] = {}
+ 883 for schema in self . _schemas [ key ]:
+ 884 if self . _schemas [ key ][ schema ] . delete ( client ):
+ 885 new_schemas [ key ][ schema ] = self . _schemas [ key ][ schema ]
+ 886 if not new_schemas [ key ]:
+ 887 del new_schemas [ key ]
+ 888 self . _schemas = new_schemas
+ 889 if self . _clients or self . _callbacks or self . _constants or self . _schemas :
+ 890 return True
+ 891 return False
+ 892
+ 893 def check ( self , client : str , message : Message ) -> bool :
+ 894 """Get if a client has a registered template matching a message.
+ 895
+ 896 >>> r = TemplateRegistry()
+ 897 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+ 898 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 899 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 900 ... print(f"{m}: {r.check('Client 1', m)}")
+ 901 {'k1': 'v1', 'k2': 'v1'}: True
+ 902 {'k1': 'v1', 'k2': 2}: True
+ 903 {'k1': 'v2', 'k2': 'v1'}: False
+ 904 {'k1': 'v2', 'k2': 2}: False
+ 905 >>> r.insert({'k2': {'type': 'integer'}}, 'Client 2')
+ 906 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 907 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 908 ... print(f"{m}: {r.check('Client 2', m)}")
+ 909 {'k1': 'v1', 'k2': 'v1'}: False
+ 910 {'k1': 'v1', 'k2': 2}: True
+ 911 {'k1': 'v2', 'k2': 'v1'}: False
+ 912 {'k1': 'v2', 'k2': 2}: True
+ 913 """
+ 914 if client in self . _clients :
+ 915 return True
+ 916 for key in self . _constants :
+ 917 if (
+ 918 key in message
+ 919 and isinstance ( message [ key ], str )
+ 920 and message [ key ] in self . _constants [ key ]
+ 921 ):
+ 922 value = message [ key ]
+ 923 assert isinstance ( value , str )
+ 924 child = self . _constants [ key ][ value ]
+ 925 if child . check ( client , message ):
+ 926 return True
+ 927 for key in self . _schemas :
+ 928 if key in message :
+ 929 for schema_string in self . _schemas [ key ]:
+ 930 if validate ( schema_string , message [ key ]):
+ 931 child = self . _schemas [ key ][ schema_string ]
+ 932 if child . check ( client , message ):
+ 933 return True
+ 934 return False
+ 935
+ 936 def get ( self , message : Message ) -> List [ str ]:
+ 937 """Get all clients registered for templates matching a message.
+ 938
+ 939 >>> r = TemplateRegistry()
+ 940 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+ 941 >>> r.insert({'k2': {'type': 'integer'}}, 'Client 2')
+ 942 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+ 943 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+ 944 ... print(f"{m}: {r.get(m)}")
+ 945 {'k1': 'v1', 'k2': 'v1'}: ['Client 1']
+ 946 {'k1': 'v1', 'k2': 2}: ['Client 1', 'Client 2']
+ 947 {'k1': 'v2', 'k2': 'v1'}: []
+ 948 {'k1': 'v2', 'k2': 2}: ['Client 2']
+ 949 """
+ 950 result = []
+ 951 for client in self . _clients :
+ 952 if client not in result :
+ 953 result . append ( client )
+ 954 for key in self . _constants :
+ 955 if (
+ 956 key in message
+ 957 and isinstance ( message [ key ], str )
+ 958 and message [ key ] in self . _constants [ key ]
+ 959 ):
+ 960 value = message [ key ]
+ 961 assert isinstance ( value , str )
+ 962 child = self . _constants [ key ][ value ]
+ 963 for client in child . get ( message ):
+ 964 if client not in result :
+ 965 result . append ( client )
+ 966 for key in self . _schemas :
+ 967 if key in message :
+ 968 for schema_string in self . _schemas [ key ]:
+ 969 if validate ( schema_string , message [ key ]):
+ 970 child = self . _schemas [ key ][ schema_string ]
+ 971 for client in child . get ( message ):
+ 972 if client not in result :
+ 973 result . append ( client )
+ 974 return result
+ 975
+ 976 def get_callbacks ( self , message : Message ) -> List [ MessageCallback ]:
+ 977 """Get all callbacks registered for templates matching a message."""
+ 978 result = []
+ 979 for client in self . _callbacks :
+ 980 for callback in self . _callbacks [ client ]:
+ 981 if callback not in result :
+ 982 result . append ( callback )
+ 983 for key in self . _constants :
+ 984 if (
+ 985 key in message
+ 986 and isinstance ( message [ key ], str )
+ 987 and message [ key ] in self . _constants [ key ]
+ 988 ):
+ 989 value = message [ key ]
+ 990 assert isinstance ( value , str )
+ 991 child = self . _constants [ key ][ value ]
+ 992 for callback in child . get_callbacks ( message ):
+ 993 if callback not in result :
+ 994 result . append ( callback )
+ 995 for key in self . _schemas :
+ 996 if key in message :
+ 997 for schema_string in self . _schemas [ key ]:
+ 998 if validate ( schema_string , message [ key ]):
+ 999 child = self . _schemas [ key ][ schema_string ]
+1000 for callback in child . get_callbacks ( message ):
+1001 if callback not in result :
+1002 result . append ( callback )
+1003 return result
+1004
+1005 def get_templates ( self , client : str ) -> List [ MessageTemplate ]:
+1006 """Get all templates for a client.
+1007
+1008 >>> r = TemplateRegistry()
+1009 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+1010 >>> r.get_templates('Client 1')
+1011 [{'k1': {'const': 'v1'}}]
+1012 >>> r.insert({'k1': {'const': 'v2'},
+1013 ... 'k2': {'type': 'string'}}, 'Client 2')
+1014 >>> r.get_templates('Client 2')
+1015 [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
+1016 >>> r.insert({'k1': {'const': 'v2'},
+1017 ... 'k2': {'type': 'integer'}}, 'Client 3')
+1018 >>> r.get_templates('Client 3')
+1019 [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
+1020 >>> r.insert({'k2': {'const': 2}}, 'Client 4')
+1021 >>> r.get_templates('Client 4')
+1022 [{'k2': {'const': 2}}]
+1023 >>> r.insert({}, 'Client 5')
+1024 >>> r.get_templates('Client 5')
+1025 [{}]
+1026 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 6')
+1027 >>> r.insert({'k2': {'const': 'v1'}}, 'Client 6')
+1028 >>> r.get_templates('Client 6')
+1029 [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
+1030 """
+1031 if client in self . _templates :
+1032 return self . _templates [ client ]
+1033 return []
+
+
+
+ Manage a collection of message templates with registered clients.
+
+
A new TemplateRegistry is created by:
+
+
+
>>> r = TemplateRegistry ()
+
+
+
+
Client names (strings) can be registered for message templates, which
+are mappings from keys to JSON schemas:
+
+
+
>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'C 1' )
+
+
+
+
The check function checks if the templates registered for a client
+match a given message:
+
+
+
>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'C 1' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: True
+{'k1': 'v1', 'k2': 2}: True
+{'k1': 'v2', 'k2': 'v1'}: False
+{'k1': 'v2', 'k2': 2}: False
+
+
+
+
Clients can be registered for values validating against arbitrary JSON
+schemas, e.g. all values of a certain type:
+
+
+
>>> r . insert ({ 'k1' : { 'const' : 'v2' }, 'k2' : { 'type' : 'string' }}, 'C 2' )
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'C 2' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: False
+{'k1': 'v1', 'k2': 2}: False
+{'k1': 'v2', 'k2': 'v1'}: True
+{'k1': 'v2', 'k2': 2}: False
+>>> r . insert ({ 'k1' : { 'const' : 'v2' }, 'k2' : { 'type' : 'integer' }}, 'C 3' )
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'C 3' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: False
+{'k1': 'v1', 'k2': 2}: False
+{'k1': 'v2', 'k2': 'v1'}: False
+{'k1': 'v2', 'k2': 2}: True
+
+
+
+
The order of key-value pairs does not have to match the order in the
+messages and keys can be left out:
+
+
+
>>> r . insert ({ 'k2' : { 'const' : 2 }}, 'C 4' )
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'C 4' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: False
+{'k1': 'v1', 'k2': 2}: True
+{'k1': 'v2', 'k2': 'v1'}: False
+{'k1': 'v2', 'k2': 2}: True
+
+
+
+
A registration for an empty template matches all messages:
+
+
+
>>> r . insert ({}, 'C 5' )
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'C 5' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: True
+{'k1': 'v1', 'k2': 2}: True
+{'k1': 'v2', 'k2': 'v1'}: True
+{'k1': 'v2', 'k2': 2}: True
+
+
+
+
A client can be registered for multiple templates:
+
+
+
>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'C 6' )
+>>> r . insert ({ 'k2' : { 'const' : 'v1' }}, 'C 6' )
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'C 6' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: True
+{'k1': 'v1', 'k2': 2}: True
+{'k1': 'v2', 'k2': 'v1'}: True
+{'k1': 'v2', 'k2': 2}: False
+
+
+
+
Clients can be deregistered again (the result is False if the registry
+is empty after the deletion):
+
+
+
>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'C 7' )
+>>> r . delete ( 'C 7' )
+True
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'C 7' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: False
+{'k1': 'v1', 'k2': 2}: False
+{'k1': 'v2', 'k2': 'v1'}: False
+{'k1': 'v2', 'k2': 2}: False
+
+
+
+
The get function returns all clients with registered templates matching
+a given message:
+
+
+
>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . get ( m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: ['C 5', 'C 1', 'C 6']
+{'k1': 'v1', 'k2': 2}: ['C 5', 'C 1', 'C 6', 'C 4']
+{'k1': 'v2', 'k2': 'v1'}: ['C 5', 'C 2', 'C 6']
+{'k1': 'v2', 'k2': 2}: ['C 5', 'C 3', 'C 4']
+
+
+
+
The get_templates function returns all templates for a given client:
+
+
+
>>> for c in [ 'C 1' , 'C 2' , 'C 3' , 'C 4' , 'C 5' , 'C 6' ]:
+... print ( f " { c } : { r . get_templates ( c ) } " )
+C 1: [{'k1': {'const': 'v1'}}]
+C 2: [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
+C 3: [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
+C 4: [{'k2': {'const': 2}}]
+C 5: [{}]
+C 6: [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
+
+
+
+
+
+
+
+
+
+ TemplateRegistry ()
+
+ View Source
+
+
+
+
788 def __init__ ( self ) -> None :
+789 """Initialise an empty registry.
+790
+791 >>> r = TemplateRegistry()
+792 """
+793 self . _clients : List [ str ] = []
+794 self . _callbacks : Dict [ str , List [ MessageCallback ]] = {}
+795 self . _constants : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+796 # First key is the message key, second key is the constant string
+797 self . _schemas : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+798 # First key is the message key, second key is the JSON schema string
+799 self . _templates : Dict [ str , List [ MessageTemplate ]] = {}
+
+
+
+
Initialise an empty registry.
+
+
+
>>> r = TemplateRegistry ()
+
+
+
+
+
+
+
+
+
+
+
def
+
insert ( self , template : MessageTemplate , client : str , callback : Callable [[ Message ], Coroutine [ Any , Any , NoneType ]] | None = None ) -> None :
+
+
View Source
+
+
+
+
801 def insert (
+802 self ,
+803 template : MessageTemplate ,
+804 client : str ,
+805 callback : Optional [ MessageCallback ] = None ,
+806 ) -> None :
+807 """Register a client for a template.
+808
+809 >>> r = TemplateRegistry()
+810 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'integer'}}, 'C 1')
+811 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'string'}}, 'C 2')
+812 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v1'}}, 'C 3')
+813 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v2'}}, 'C 4')
+814 >>> r.insert({}, 'C 5')
+815 """
+816 if client not in self . _templates :
+817 self . _templates [ client ] = []
+818 self . _templates [ client ] . append ( template )
+819 if not template :
+820 self . _clients . append ( client )
+821 if callback :
+822 if client not in self . _callbacks :
+823 self . _callbacks [ client ] = []
+824 self . _callbacks [ client ] . append ( callback )
+825 else :
+826 key , schema = next ( iter ( template . items ()))
+827 reduced_template = MessageTemplate (
+828 { k : template [ k ] for k in template if k != key }
+829 )
+830 if (
+831 isinstance ( schema , dict )
+832 and len ( schema ) == 1
+833 and "const" in schema
+834 and isinstance ( schema [ "const" ], str )
+835 ):
+836 value = schema [ "const" ]
+837 if key not in self . _constants :
+838 self . _constants [ key ] = {}
+839 if value not in self . _constants [ key ]:
+840 self . _constants [ key ][ value ] = TemplateRegistry ()
+841 self . _constants [ key ][ value ] . insert ( reduced_template , client , callback )
+842 else :
+843 schema_string = json . dumps ( schema )
+844 if key not in self . _schemas :
+845 self . _schemas [ key ] = {}
+846 if schema_string not in self . _schemas [ key ]:
+847 self . _schemas [ key ][ schema_string ] = TemplateRegistry ()
+848 self . _schemas [ key ][ schema_string ] . insert (
+849 reduced_template , client , callback
+850 )
+
+
+
+
Register a client for a template.
+
+
+
>>> r = TemplateRegistry ()
+>>> r . insert ({ 'k1' : { 'const' : 'v1' }, 'k2' : { 'type' : 'integer' }}, 'C 1' )
+>>> r . insert ({ 'k1' : { 'const' : 'v1' }, 'k2' : { 'type' : 'string' }}, 'C 2' )
+>>> r . insert ({ 'k1' : { 'type' : 'integer' }, 'k2' : { 'const' : 'v1' }}, 'C 3' )
+>>> r . insert ({ 'k1' : { 'type' : 'integer' }, 'k2' : { 'const' : 'v2' }}, 'C 4' )
+>>> r . insert ({}, 'C 5' )
+
+
+
+
+
+
+
+
+
+
+ def
+ delete (self , client : str ) -> bool :
+
+ View Source
+
+
+
+
852 def delete ( self , client : str ) -> bool :
+853 """Unregister a client from all templates.
+854
+855 >>> r = TemplateRegistry()
+856 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'integer'}}, 'C 1')
+857 >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'type': 'string'}}, 'C 2')
+858 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v1'}}, 'C 3')
+859 >>> r.insert({'k1': {'type': 'integer'}, 'k2': {'const': 'v2'}}, 'C 4')
+860 >>> r.insert({}, 'C 5')
+861 >>> r.delete('C 3')
+862 True
+863 >>> r.delete('C 4')
+864 True
+865 """
+866 if client in self . _templates :
+867 del self . _templates [ client ]
+868 self . _clients = [ c for c in self . _clients if c != client ]
+869 if client in self . _callbacks :
+870 del self . _callbacks [ client ]
+871 new_constants : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+872 for key in self . _constants :
+873 new_constants [ key ] = {}
+874 for value in self . _constants [ key ]:
+875 if self . _constants [ key ][ value ] . delete ( client ):
+876 new_constants [ key ][ value ] = self . _constants [ key ][ value ]
+877 if not new_constants [ key ]:
+878 del new_constants [ key ]
+879 self . _constants = new_constants
+880 new_schemas : Dict [ str , Dict [ str , TemplateRegistry ]] = {}
+881 for key in self . _schemas :
+882 new_schemas [ key ] = {}
+883 for schema in self . _schemas [ key ]:
+884 if self . _schemas [ key ][ schema ] . delete ( client ):
+885 new_schemas [ key ][ schema ] = self . _schemas [ key ][ schema ]
+886 if not new_schemas [ key ]:
+887 del new_schemas [ key ]
+888 self . _schemas = new_schemas
+889 if self . _clients or self . _callbacks or self . _constants or self . _schemas :
+890 return True
+891 return False
+
+
+
+
Unregister a client from all templates.
+
+
+
>>> r = TemplateRegistry ()
+>>> r . insert ({ 'k1' : { 'const' : 'v1' }, 'k2' : { 'type' : 'integer' }}, 'C 1' )
+>>> r . insert ({ 'k1' : { 'const' : 'v1' }, 'k2' : { 'type' : 'string' }}, 'C 2' )
+>>> r . insert ({ 'k1' : { 'type' : 'integer' }, 'k2' : { 'const' : 'v1' }}, 'C 3' )
+>>> r . insert ({ 'k1' : { 'type' : 'integer' }, 'k2' : { 'const' : 'v2' }}, 'C 4' )
+>>> r . insert ({}, 'C 5' )
+>>> r . delete ( 'C 3' )
+True
+>>> r . delete ( 'C 4' )
+True
+
+
+
+
+
+
+
+
+
+
+
def
+
check (self , client : str , message : Message ) -> bool :
+
+
View Source
+
+
+
+
893 def check ( self , client : str , message : Message ) -> bool :
+894 """Get if a client has a registered template matching a message.
+895
+896 >>> r = TemplateRegistry()
+897 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+898 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+899 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+900 ... print(f"{m}: {r.check('Client 1', m)}")
+901 {'k1': 'v1', 'k2': 'v1'}: True
+902 {'k1': 'v1', 'k2': 2}: True
+903 {'k1': 'v2', 'k2': 'v1'}: False
+904 {'k1': 'v2', 'k2': 2}: False
+905 >>> r.insert({'k2': {'type': 'integer'}}, 'Client 2')
+906 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+907 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+908 ... print(f"{m}: {r.check('Client 2', m)}")
+909 {'k1': 'v1', 'k2': 'v1'}: False
+910 {'k1': 'v1', 'k2': 2}: True
+911 {'k1': 'v2', 'k2': 'v1'}: False
+912 {'k1': 'v2', 'k2': 2}: True
+913 """
+914 if client in self . _clients :
+915 return True
+916 for key in self . _constants :
+917 if (
+918 key in message
+919 and isinstance ( message [ key ], str )
+920 and message [ key ] in self . _constants [ key ]
+921 ):
+922 value = message [ key ]
+923 assert isinstance ( value , str )
+924 child = self . _constants [ key ][ value ]
+925 if child . check ( client , message ):
+926 return True
+927 for key in self . _schemas :
+928 if key in message :
+929 for schema_string in self . _schemas [ key ]:
+930 if validate ( schema_string , message [ key ]):
+931 child = self . _schemas [ key ][ schema_string ]
+932 if child . check ( client , message ):
+933 return True
+934 return False
+
+
+
+
Get if a client has a registered template matching a message.
+
+
+
>>> r = TemplateRegistry ()
+>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'Client 1' )
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'Client 1' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: True
+{'k1': 'v1', 'k2': 2}: True
+{'k1': 'v2', 'k2': 'v1'}: False
+{'k1': 'v2', 'k2': 2}: False
+>>> r . insert ({ 'k2' : { 'type' : 'integer' }}, 'Client 2' )
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . check ( 'Client 2' , m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: False
+{'k1': 'v1', 'k2': 2}: True
+{'k1': 'v2', 'k2': 'v1'}: False
+{'k1': 'v2', 'k2': 2}: True
+
+
+
+
+
+
+
+
+
+
+
def
+
get (self , message : Message ) -> List [ str ] :
+
+
View Source
+
+
+
+
936 def get ( self , message : Message ) -> List [ str ]:
+937 """Get all clients registered for templates matching a message.
+938
+939 >>> r = TemplateRegistry()
+940 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+941 >>> r.insert({'k2': {'type': 'integer'}}, 'Client 2')
+942 >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
+943 ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
+944 ... print(f"{m}: {r.get(m)}")
+945 {'k1': 'v1', 'k2': 'v1'}: ['Client 1']
+946 {'k1': 'v1', 'k2': 2}: ['Client 1', 'Client 2']
+947 {'k1': 'v2', 'k2': 'v1'}: []
+948 {'k1': 'v2', 'k2': 2}: ['Client 2']
+949 """
+950 result = []
+951 for client in self . _clients :
+952 if client not in result :
+953 result . append ( client )
+954 for key in self . _constants :
+955 if (
+956 key in message
+957 and isinstance ( message [ key ], str )
+958 and message [ key ] in self . _constants [ key ]
+959 ):
+960 value = message [ key ]
+961 assert isinstance ( value , str )
+962 child = self . _constants [ key ][ value ]
+963 for client in child . get ( message ):
+964 if client not in result :
+965 result . append ( client )
+966 for key in self . _schemas :
+967 if key in message :
+968 for schema_string in self . _schemas [ key ]:
+969 if validate ( schema_string , message [ key ]):
+970 child = self . _schemas [ key ][ schema_string ]
+971 for client in child . get ( message ):
+972 if client not in result :
+973 result . append ( client )
+974 return result
+
+
+
+
Get all clients registered for templates matching a message.
+
+
+
>>> r = TemplateRegistry ()
+>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'Client 1' )
+>>> r . insert ({ 'k2' : { 'type' : 'integer' }}, 'Client 2' )
+>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 },
+... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]:
+... print ( f " { m } : { r . get ( m ) } " )
+{'k1': 'v1', 'k2': 'v1'}: ['Client 1']
+{'k1': 'v1', 'k2': 2}: ['Client 1', 'Client 2']
+{'k1': 'v2', 'k2': 'v1'}: []
+{'k1': 'v2', 'k2': 2}: ['Client 2']
+
+
+
+
+
+
+
+
+
+
+
def
+
get_callbacks ( self , message : Message ) -> List [ Callable [[ Message ], Coroutine [ Any , Any , NoneType ]]] :
+
+
View Source
+
+
+
+
976 def get_callbacks ( self , message : Message ) -> List [ MessageCallback ]:
+ 977 """Get all callbacks registered for templates matching a message."""
+ 978 result = []
+ 979 for client in self . _callbacks :
+ 980 for callback in self . _callbacks [ client ]:
+ 981 if callback not in result :
+ 982 result . append ( callback )
+ 983 for key in self . _constants :
+ 984 if (
+ 985 key in message
+ 986 and isinstance ( message [ key ], str )
+ 987 and message [ key ] in self . _constants [ key ]
+ 988 ):
+ 989 value = message [ key ]
+ 990 assert isinstance ( value , str )
+ 991 child = self . _constants [ key ][ value ]
+ 992 for callback in child . get_callbacks ( message ):
+ 993 if callback not in result :
+ 994 result . append ( callback )
+ 995 for key in self . _schemas :
+ 996 if key in message :
+ 997 for schema_string in self . _schemas [ key ]:
+ 998 if validate ( schema_string , message [ key ]):
+ 999 child = self . _schemas [ key ][ schema_string ]
+1000 for callback in child . get_callbacks ( message ):
+1001 if callback not in result :
+1002 result . append ( callback )
+1003 return result
+
+
+
+
Get all callbacks registered for templates matching a message.
+
+
+
+
+
+
+
+
+
def
+
get_templates (self , client : str ) -> List [ MessageTemplate ] :
+
+
View Source
+
+
+
+
1005 def get_templates ( self , client : str ) -> List [ MessageTemplate ]:
+1006 """Get all templates for a client.
+1007
+1008 >>> r = TemplateRegistry()
+1009 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
+1010 >>> r.get_templates('Client 1')
+1011 [{'k1': {'const': 'v1'}}]
+1012 >>> r.insert({'k1': {'const': 'v2'},
+1013 ... 'k2': {'type': 'string'}}, 'Client 2')
+1014 >>> r.get_templates('Client 2')
+1015 [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
+1016 >>> r.insert({'k1': {'const': 'v2'},
+1017 ... 'k2': {'type': 'integer'}}, 'Client 3')
+1018 >>> r.get_templates('Client 3')
+1019 [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
+1020 >>> r.insert({'k2': {'const': 2}}, 'Client 4')
+1021 >>> r.get_templates('Client 4')
+1022 [{'k2': {'const': 2}}]
+1023 >>> r.insert({}, 'Client 5')
+1024 >>> r.get_templates('Client 5')
+1025 [{}]
+1026 >>> r.insert({'k1': {'const': 'v1'}}, 'Client 6')
+1027 >>> r.insert({'k2': {'const': 'v1'}}, 'Client 6')
+1028 >>> r.get_templates('Client 6')
+1029 [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
+1030 """
+1031 if client in self . _templates :
+1032 return self . _templates [ client ]
+1033 return []
+
+
+
+
Get all templates for a client.
+
+
+
>>> r = TemplateRegistry ()
+>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'Client 1' )
+>>> r . get_templates ( 'Client 1' )
+[{'k1': {'const': 'v1'}}]
+>>> r . insert ({ 'k1' : { 'const' : 'v2' },
+... 'k2' : { 'type' : 'string' }}, 'Client 2' )
+>>> r . get_templates ( 'Client 2' )
+[{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
+>>> r . insert ({ 'k1' : { 'const' : 'v2' },
+... 'k2' : { 'type' : 'integer' }}, 'Client 3' )
+>>> r . get_templates ( 'Client 3' )
+[{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
+>>> r . insert ({ 'k2' : { 'const' : 2 }}, 'Client 4' )
+>>> r . get_templates ( 'Client 4' )
+[{'k2': {'const': 2}}]
+>>> r . insert ({}, 'Client 5' )
+>>> r . get_templates ( 'Client 5' )
+[{}]
+>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'Client 6' )
+>>> r . insert ({ 'k2' : { 'const' : 'v1' }}, 'Client 6' )
+>>> r . get_templates ( 'Client 6' )
+[{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
+
+
+
+
+
+
+
+
+
+
+
+ class
+ BusException (builtins.Exception ):
+
+ View Source
+
+
+
+ 1036 class BusException ( Exception ):
+1037 """Raise for errors in using message bus."""
+
+
+
+ Raise for errors in using message bus.
+
+
+
+
+
+
+
+
+ class
+ MessageBus :
+
+ View Source
+
+
+
+ 1040 class MessageBus :
+1041 """Provide an asynchronous message bus.
+1042
+1043 The bus executes asynchronous callbacks for all messages to be received
+1044 by a client. We use a simple callback printing the message in all
+1045 examples:
+1046 >>> def callback_for_receiver(receiver):
+1047 ... print(f"Creating callback for {receiver}.")
+1048 ... async def callback(message):
+1049 ... print(f"{receiver}: {message}")
+1050 ... return callback
+1051
+1052 Clients can be registered at the bus with a name, lists of message
+1053 templates they want to use for sending and receiving and a callback
+1054 function for receiving. An empty list of templates means that the
+1055 client does not want to send or receive any messages, respectively.
+1056 A list with an empty template means that it wants to send arbitrary
+1057 or receive all messages, respectively:
+1058 >>> async def setup(bus):
+1059 ... print("Setting up.")
+1060 ... bus.register('Logger', 'Test Plugin',
+1061 ... [],
+1062 ... [([MessageTemplate({})],
+1063 ... callback_for_receiver('Logger'))])
+1064 ... bus.register('Client 1', 'Test Plugin',
+1065 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1066 ... [([MessageTemplate({'target':
+1067 ... {'const': 'Client 1'}})],
+1068 ... callback_for_receiver('Client 1'))])
+1069 ... bus.register('Client 2', 'Test Plugin',
+1070 ... [MessageTemplate({})],
+1071 ... [([MessageTemplate({'target':
+1072 ... {'const': 'Client 2'}})],
+1073 ... callback_for_receiver('Client 2'))])
+1074
+1075 The bus itself is addressed by the empty string. It sends messages for
+1076 each registration and deregestration of a client with a key 'event' and
+1077 a value of 'registered' or 'unregistered', a key 'client' with the
+1078 client's name as value and for registrations also keys 'sends' and
+1079 'receives' with all templates registered for the client for sending and
+1080 receiving.
+1081
+1082 Clients can send to the bus with the send function. Each message has to
+1083 declare a sender. The send templates of that sender are checked for a
+1084 template matching the message. We cannot prevent arbitrary code from
+1085 impersonating any sender, but this should only be done in debugging or
+1086 management situations.
+1087
+1088 Messages that are intended for a specific client by convention have a
+1089 key 'target' with the target client's name as value. Such messages are
+1090 often commands to the client to do something, which is by convention
+1091 indicated by a key 'command' with a value that indicates what should be
+1092 done.
+1093
+1094 The bus, for example, reacts to a message with 'target': '' and
+1095 'command': 'get clients' by sending one message for each currently
+1096 registered with complete information about its registered send and
+1097 receive templates.
+1098
+1099 >>> async def send(bus):
+1100 ... print("Sending messages.")
+1101 ... await bus.send({'sender': 'Client 1', 'k1': 'Test'})
+1102 ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
+1103 ... await bus.send({'sender': '', 'target': '',
+1104 ... 'command': 'get clients'})
+1105
+1106 The run function executes the message bus forever. If we want to stop
+1107 it, we have to explicitly cancel the task:
+1108 >>> async def main():
+1109 ... bus = MessageBus()
+1110 ... await setup(bus)
+1111 ... bus_task = asyncio.create_task(bus.run())
+1112 ... await send(bus)
+1113 ... await asyncio.sleep(0)
+1114 ... bus_task.cancel()
+1115 ... try:
+1116 ... await bus_task
+1117 ... except asyncio.exceptions.CancelledError:
+1118 ... pass
+1119 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+1120 Setting up.
+1121 Creating callback for Logger.
+1122 Creating callback for Client 1.
+1123 Creating callback for Client 2.
+1124 Sending messages.
+1125 Logger: {'sender': '', 'event': 'registered',
+1126 'client': 'Logger', 'plugin': 'Test Plugin',
+1127 'sends': [], 'receives': [{}]}
+1128 Logger: {'sender': '', 'event': 'registered',
+1129 'client': 'Client 1', 'plugin': 'Test Plugin',
+1130 'sends': [{'k1': {'type': 'string'}}],
+1131 'receives': [{'target': {'const': 'Client 1'}}]}
+1132 Logger: {'sender': '', 'event': 'registered',
+1133 'client': 'Client 2', 'plugin': 'Test Plugin',
+1134 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
+1135 Logger: {'sender': 'Client 1', 'k1': 'Test'}
+1136 Logger: {'sender': 'Client 2', 'target': 'Client 1'}
+1137 Client 1: {'sender': 'Client 2', 'target': 'Client 1'}
+1138 Logger: {'sender': '', 'target': '', 'command': 'get clients'}
+1139 Logger: {'sender': '', 'client': 'Logger', 'plugin': 'Test Plugin',
+1140 'sends': [], 'receives': [{}]}
+1141 Logger: {'sender': '', 'client': 'Client 1', 'plugin': 'Test Plugin',
+1142 'sends': [{'k1': {'type': 'string'}}],
+1143 'receives': [{'target': {'const': 'Client 1'}}]}
+1144 Logger: {'sender': '', 'client': 'Client 2', 'plugin': 'Test Plugin',
+1145 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
+1146 """
+1147
+1148 def __init__ ( self ) -> None :
+1149 """Initialise a new bus without clients.
+1150
+1151 >>> async def main():
+1152 ... bus = MessageBus()
+1153 >>> asyncio.run(main())
+1154 """
+1155 self . _queue : asyncio . Queue = asyncio . Queue ()
+1156 self . _plugins : Dict [ str , str ] = {}
+1157 self . _send_reg : TemplateRegistry = TemplateRegistry ()
+1158 self . _recv_reg : TemplateRegistry = TemplateRegistry ()
+1159
+1160 def register (
+1161 self ,
+1162 client : str ,
+1163 plugin : str ,
+1164 sends : Iterable [ MessageTemplate ],
+1165 receives : Iterable [ Tuple [ Iterable [ MessageTemplate ], MessageCallback ]],
+1166 ) -> None :
+1167 """Register a client at the message bus.
+1168
+1169 >>> async def callback(message):
+1170 ... print(message)
+1171 >>> async def main():
+1172 ... bus = MessageBus()
+1173 ... bus.register('Logger', 'Test Plugin',
+1174 ... [], # send nothing
+1175 ... [([MessageTemplate({})], # receive everything
+1176 ... callback)])
+1177 ... bus.register('Client 1', 'Test Plugin',
+1178 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1179 ... # send with key 'k1' and string value
+1180 ... [([MessageTemplate({'target':
+1181 ... {'const': 'Client 1'}})],
+1182 ... # receive for this client
+1183 ... callback)])
+1184 ... bus.register('Client 2', 'Test Plugin',
+1185 ... [MessageTemplate({})], # send arbitrary
+1186 ... [([MessageTemplate({'target':
+1187 ... {'const': 'Client 2'}})],
+1188 ... # receive for this client
+1189 ... callback)])
+1190 >>> asyncio.run(main())
+1191 """
+1192 if not client :
+1193 raise BusException ( "Client name is not allowed to be empty." )
+1194 if client in self . _plugins :
+1195 raise BusException ( f "Client ' { client } ' already registered at message bus." )
+1196 event = Message ( "" )
+1197 event [ "event" ] = "registered"
+1198 event [ "client" ] = client
+1199 self . _plugins [ client ] = plugin
+1200 event [ "plugin" ] = plugin
+1201 for template in sends :
+1202 self . _send_reg . insert ( template , client )
+1203 event [ "sends" ] = self . _send_reg . get_templates ( client )
+1204 for templates , callback in receives :
+1205 for template in templates :
+1206 self . _recv_reg . insert ( template , client , callback )
+1207 event [ "receives" ] = self . _recv_reg . get_templates ( client )
+1208 self . _queue . put_nowait ( event )
+1209
+1210 def unregister ( self , client : str ) -> None :
+1211 """Unregister a client from the message bus.
+1212
+1213 >>> async def callback(message):
+1214 ... print(message)
+1215 >>> async def main():
+1216 ... bus = MessageBus()
+1217 ... bus.register('Client 1', 'Test Plugin',
+1218 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1219 ... [([MessageTemplate({'target':
+1220 ... {'const': 'Client 1'}})],
+1221 ... callback)])
+1222 ... bus.unregister('Client 1')
+1223 >>> asyncio.run(main())
+1224 """
+1225 if client not in self . _plugins :
+1226 return
+1227 event = Message ( "" )
+1228 event [ "event" ] = "unregistered"
+1229 event [ "client" ] = client
+1230 del self . _plugins [ client ]
+1231 self . _send_reg . delete ( client )
+1232 self . _recv_reg . delete ( client )
+1233 self . _queue . put_nowait ( event )
+1234
+1235 async def run ( self ) -> None :
+1236 """Run the message bus forever.
+1237
+1238 >>> async def main():
+1239 ... bus = MessageBus()
+1240 ... bus_task = asyncio.create_task(bus.run())
+1241 ... bus_task.cancel()
+1242 ... try:
+1243 ... await bus_task
+1244 ... except asyncio.exceptions.CancelledError:
+1245 ... pass
+1246 >>> asyncio.run(main())
+1247 """
+1248 background_tasks = set ()
+1249 while True :
+1250 message = await self . _queue . get ()
+1251 if "target" in message and message [ "target" ] == "" and "command" in message :
+1252 if message [ "command" ] == "get clients" :
+1253 for client in self . _plugins :
+1254 answer = Message ( "" )
+1255 answer [ "client" ] = client
+1256 answer [ "plugin" ] = self . _plugins [ client ]
+1257 answer [ "sends" ] = self . _send_reg . get_templates ( client )
+1258 answer [ "receives" ] = self . _recv_reg . get_templates ( client )
+1259 await self . _queue . put ( answer )
+1260 elif message [ "command" ] == "push conf" :
+1261 conf = {}
+1262 try :
+1263 with open ( sys . argv [ 1 ]) as conf_file :
+1264 conf = json . load ( conf_file )
+1265 except (
+1266 IndexError ,
+1267 FileNotFoundError ,
+1268 json . decoder . JSONDecodeError ,
+1269 ):
+1270 pass
+1271 if conf == message [ "conf" ]:
+1272 await self . _queue . put ( Message ( "" , { "event" : "conf unchanged" }))
+1273 else :
+1274 await self . _queue . put ( Message ( "" , { "event" : "conf changed" }))
+1275 with open ( sys . argv [ 1 ], "w" ) as conf_file :
+1276 json . dump ( message [ "conf" ], conf_file )
+1277 for callback in self . _recv_reg . get_callbacks ( message ):
+1278 task = asyncio . create_task ( callback ( message ))
+1279 background_tasks . add ( task )
+1280 task . add_done_callback ( background_tasks . discard )
+1281 self . _queue . task_done ()
+1282
+1283 async def send ( self , message : Message ) -> None :
+1284 """Send a message to the message bus.
+1285
+1286 >>> async def callback(message):
+1287 ... print(f"Got: {message}")
+1288 >>> async def main():
+1289 ... bus = MessageBus()
+1290 ... bus.register('Client 1', 'Test Plugin',
+1291 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1292 ... [([MessageTemplate({'target':
+1293 ... {'const': 'Client 1'}})],
+1294 ... callback)])
+1295 ... bus.register('Client 2', 'Test Plugin',
+1296 ... [MessageTemplate({})],
+1297 ... [([MessageTemplate({'target':
+1298 ... {'const': 'Client 2'}})],
+1299 ... callback)])
+1300 ... bus_task = asyncio.create_task(bus.run())
+1301 ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
+1302 ... 'k1': 'Test'})
+1303 ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
+1304 ... try:
+1305 ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
+1306 ... 'k1': 42})
+1307 ... except BusException as e:
+1308 ... print(e)
+1309 ... await asyncio.sleep(0)
+1310 ... bus_task.cancel()
+1311 ... try:
+1312 ... await bus_task
+1313 ... except asyncio.exceptions.CancelledError:
+1314 ... pass
+1315 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+1316 Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
+1317 not allowed for sender 'Client 1'.
+1318 Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
+1319 Got: {'sender': 'Client 2', 'target': 'Client 1'}
+1320 """
+1321 assert isinstance ( message [ "sender" ], str )
+1322 sender = message [ "sender" ]
+1323 if sender :
+1324 if not self . _send_reg . check ( sender , message ):
+1325 raise BusException (
+1326 f "Message ' { message } ' not allowed for sender ' { sender } '."
+1327 )
+1328 await self . _queue . put ( message )
+1329
+1330 def send_nowait ( self , message : Message ) -> None :
+1331 """Send a message to the message bus without blocking.
+1332
+1333 >>> async def callback(message):
+1334 ... print(f"Got: {message}")
+1335 >>> async def main():
+1336 ... bus = MessageBus()
+1337 ... bus.register('Client 1', 'Test Plugin',
+1338 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1339 ... [([MessageTemplate({'target':
+1340 ... {'const': 'Client 1'}})],
+1341 ... callback)])
+1342 ... bus.register('Client 2', 'Test Plugin',
+1343 ... [MessageTemplate({})],
+1344 ... [([MessageTemplate({'target':
+1345 ... {'const': 'Client 2'}})],
+1346 ... callback)])
+1347 ... bus_task = asyncio.create_task(bus.run())
+1348 ... bus.send_nowait({'sender': 'Client 1', 'target': 'Client 2',
+1349 ... 'k1': 'Test'})
+1350 ... bus.send_nowait({'sender': 'Client 2', 'target': 'Client 1'})
+1351 ... try:
+1352 ... bus.send_nowait({'sender': 'Client 1',
+1353 ... 'target': 'Client 2', 'k1': 42})
+1354 ... except BusException as e:
+1355 ... print(e)
+1356 ... await asyncio.sleep(0)
+1357 ... bus_task.cancel()
+1358 ... try:
+1359 ... await bus_task
+1360 ... except asyncio.exceptions.CancelledError:
+1361 ... pass
+1362 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+1363 Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
+1364 not allowed for sender 'Client 1'.
+1365 Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
+1366 Got: {'sender': 'Client 2', 'target': 'Client 1'}
+1367 """
+1368 assert isinstance ( message [ "sender" ], str )
+1369 sender = message [ "sender" ]
+1370 if sender :
+1371 if not self . _send_reg . check ( sender , message ):
+1372 raise BusException (
+1373 f "Message ' { message } ' not allowed for sender ' { sender } '."
+1374 )
+1375 self . _queue . put_nowait ( message )
+
+
+
+ Provide an asynchronous message bus.
+
+
The bus executes asynchronous callbacks for all messages to be received
+by a client. We use a simple callback printing the message in all
+examples:
+
+
+
>>> def callback_for_receiver ( receiver ):
+... print ( f "Creating callback for { receiver } ." )
+... async def callback ( message ):
+... print ( f " { receiver } : { message } " )
+... return callback
+
+
+
+
Clients can be registered at the bus with a name, lists of message
+templates they want to use for sending and receiving and a callback
+function for receiving. An empty list of templates means that the
+client does not want to send or receive any messages, respectively.
+A list with an empty template means that it wants to send arbitrary
+or receive all messages, respectively:
+
+
+
>>> async def setup ( bus ):
+... print ( "Setting up." )
+... bus . register ( 'Logger' , 'Test Plugin' ,
+... [],
+... [([ MessageTemplate ({})],
+... callback_for_receiver ( 'Logger' ))])
+... bus . register ( 'Client 1' , 'Test Plugin' ,
+... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})],
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 1' }})],
+... callback_for_receiver ( 'Client 1' ))])
+... bus . register ( 'Client 2' , 'Test Plugin' ,
+... [ MessageTemplate ({})],
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 2' }})],
+... callback_for_receiver ( 'Client 2' ))])
+
+
+
+
The bus itself is addressed by the empty string. It sends messages for
+each registration and deregestration of a client with a key 'event' and
+a value of 'registered' or 'unregistered', a key 'client' with the
+client's name as value and for registrations also keys 'sends' and
+'receives' with all templates registered for the client for sending and
+receiving.
+
+
Clients can send to the bus with the send function. Each message has to
+declare a sender. The send templates of that sender are checked for a
+template matching the message. We cannot prevent arbitrary code from
+impersonating any sender, but this should only be done in debugging or
+management situations.
+
+
Messages that are intended for a specific client by convention have a
+key 'target' with the target client's name as value. Such messages are
+often commands to the client to do something, which is by convention
+indicated by a key 'command' with a value that indicates what should be
+done.
+
+
The bus, for example, reacts to a message with 'target': '' and
+'command': 'get clients' by sending one message for each currently
+registered with complete information about its registered send and
+receive templates.
+
+
+
>>> async def send ( bus ):
+... print ( "Sending messages." )
+... await bus . send ({ 'sender' : 'Client 1' , 'k1' : 'Test' })
+... await bus . send ({ 'sender' : 'Client 2' , 'target' : 'Client 1' })
+... await bus . send ({ 'sender' : '' , 'target' : '' ,
+... 'command' : 'get clients' })
+
+
+
+
The run function executes the message bus forever. If we want to stop
+it, we have to explicitly cancel the task:
+
+
+
>>> async def main ():
+... bus = MessageBus ()
+... await setup ( bus )
+... bus_task = asyncio . create_task ( bus . run ())
+... await send ( bus )
+... await asyncio . sleep ( 0 )
+... bus_task . cancel ()
+... try :
+... await bus_task
+... except asyncio . exceptions . CancelledError :
+... pass
+>>> asyncio . run ( main ()) # doctest: +NORMALIZE_WHITESPACE
+Setting up.
+Creating callback for Logger.
+Creating callback for Client 1.
+Creating callback for Client 2.
+Sending messages.
+Logger: {'sender': '', 'event': 'registered',
+ 'client': 'Logger', 'plugin': 'Test Plugin',
+ 'sends': [], 'receives': [{}]}
+Logger: {'sender': '', 'event': 'registered',
+ 'client': 'Client 1', 'plugin': 'Test Plugin',
+ 'sends': [{'k1': {'type': 'string'}}],
+ 'receives': [{'target': {'const': 'Client 1'}}]}
+Logger: {'sender': '', 'event': 'registered',
+ 'client': 'Client 2', 'plugin': 'Test Plugin',
+ 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
+Logger: {'sender': 'Client 1', 'k1': 'Test'}
+Logger: {'sender': 'Client 2', 'target': 'Client 1'}
+Client 1: {'sender': 'Client 2', 'target': 'Client 1'}
+Logger: {'sender': '', 'target': '', 'command': 'get clients'}
+Logger: {'sender': '', 'client': 'Logger', 'plugin': 'Test Plugin',
+ 'sends': [], 'receives': [{}]}
+Logger: {'sender': '', 'client': 'Client 1', 'plugin': 'Test Plugin',
+ 'sends': [{'k1': {'type': 'string'}}],
+ 'receives': [{'target': {'const': 'Client 1'}}]}
+Logger: {'sender': '', 'client': 'Client 2', 'plugin': 'Test Plugin',
+ 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
+
+
+
+
+
+
+
+
+
+ MessageBus ()
+
+ View Source
+
+
+
+
1148 def __init__ ( self ) -> None :
+1149 """Initialise a new bus without clients.
+1150
+1151 >>> async def main():
+1152 ... bus = MessageBus()
+1153 >>> asyncio.run(main())
+1154 """
+1155 self . _queue : asyncio . Queue = asyncio . Queue ()
+1156 self . _plugins : Dict [ str , str ] = {}
+1157 self . _send_reg : TemplateRegistry = TemplateRegistry ()
+1158 self . _recv_reg : TemplateRegistry = TemplateRegistry ()
+
+
+
+
Initialise a new bus without clients.
+
+
+
>>> async def main ():
+... bus = MessageBus ()
+>>> asyncio . run ( main ())
+
+
+
+
+
+
+
+
+
+
+
def
+
register ( self , client : str , plugin : str , sends : Iterable [ MessageTemplate ] , receives : Iterable [ Tuple [ Iterable [ MessageTemplate ], Callable [[ Message ], Coroutine [ Any , Any , NoneType ]]]] ) -> None :
+
+
View Source
+
+
+
+
1160 def register (
+1161 self ,
+1162 client : str ,
+1163 plugin : str ,
+1164 sends : Iterable [ MessageTemplate ],
+1165 receives : Iterable [ Tuple [ Iterable [ MessageTemplate ], MessageCallback ]],
+1166 ) -> None :
+1167 """Register a client at the message bus.
+1168
+1169 >>> async def callback(message):
+1170 ... print(message)
+1171 >>> async def main():
+1172 ... bus = MessageBus()
+1173 ... bus.register('Logger', 'Test Plugin',
+1174 ... [], # send nothing
+1175 ... [([MessageTemplate({})], # receive everything
+1176 ... callback)])
+1177 ... bus.register('Client 1', 'Test Plugin',
+1178 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1179 ... # send with key 'k1' and string value
+1180 ... [([MessageTemplate({'target':
+1181 ... {'const': 'Client 1'}})],
+1182 ... # receive for this client
+1183 ... callback)])
+1184 ... bus.register('Client 2', 'Test Plugin',
+1185 ... [MessageTemplate({})], # send arbitrary
+1186 ... [([MessageTemplate({'target':
+1187 ... {'const': 'Client 2'}})],
+1188 ... # receive for this client
+1189 ... callback)])
+1190 >>> asyncio.run(main())
+1191 """
+1192 if not client :
+1193 raise BusException ( "Client name is not allowed to be empty." )
+1194 if client in self . _plugins :
+1195 raise BusException ( f "Client ' { client } ' already registered at message bus." )
+1196 event = Message ( "" )
+1197 event [ "event" ] = "registered"
+1198 event [ "client" ] = client
+1199 self . _plugins [ client ] = plugin
+1200 event [ "plugin" ] = plugin
+1201 for template in sends :
+1202 self . _send_reg . insert ( template , client )
+1203 event [ "sends" ] = self . _send_reg . get_templates ( client )
+1204 for templates , callback in receives :
+1205 for template in templates :
+1206 self . _recv_reg . insert ( template , client , callback )
+1207 event [ "receives" ] = self . _recv_reg . get_templates ( client )
+1208 self . _queue . put_nowait ( event )
+
+
+
+
Register a client at the message bus.
+
+
+
>>> async def callback ( message ):
+... print ( message )
+>>> async def main ():
+... bus = MessageBus ()
+... bus . register ( 'Logger' , 'Test Plugin' ,
+... [], # send nothing
+... [([ MessageTemplate ({})], # receive everything
+... callback )])
+... bus . register ( 'Client 1' , 'Test Plugin' ,
+... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})],
+... # send with key 'k1' and string value
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 1' }})],
+... # receive for this client
+... callback )])
+... bus . register ( 'Client 2' , 'Test Plugin' ,
+... [ MessageTemplate ({})], # send arbitrary
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 2' }})],
+... # receive for this client
+... callback )])
+>>> asyncio . run ( main ())
+
+
+
+
+
+
+
+
+
+
+ def
+ unregister (self , client : str ) -> None :
+
+ View Source
+
+
+
+
1210 def unregister ( self , client : str ) -> None :
+1211 """Unregister a client from the message bus.
+1212
+1213 >>> async def callback(message):
+1214 ... print(message)
+1215 >>> async def main():
+1216 ... bus = MessageBus()
+1217 ... bus.register('Client 1', 'Test Plugin',
+1218 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1219 ... [([MessageTemplate({'target':
+1220 ... {'const': 'Client 1'}})],
+1221 ... callback)])
+1222 ... bus.unregister('Client 1')
+1223 >>> asyncio.run(main())
+1224 """
+1225 if client not in self . _plugins :
+1226 return
+1227 event = Message ( "" )
+1228 event [ "event" ] = "unregistered"
+1229 event [ "client" ] = client
+1230 del self . _plugins [ client ]
+1231 self . _send_reg . delete ( client )
+1232 self . _recv_reg . delete ( client )
+1233 self . _queue . put_nowait ( event )
+
+
+
+
Unregister a client from the message bus.
+
+
+
>>> async def callback ( message ):
+... print ( message )
+>>> async def main ():
+... bus = MessageBus ()
+... bus . register ( 'Client 1' , 'Test Plugin' ,
+... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})],
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 1' }})],
+... callback )])
+... bus . unregister ( 'Client 1' )
+>>> asyncio . run ( main ())
+
+
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
1235 async def run ( self ) -> None :
+1236 """Run the message bus forever.
+1237
+1238 >>> async def main():
+1239 ... bus = MessageBus()
+1240 ... bus_task = asyncio.create_task(bus.run())
+1241 ... bus_task.cancel()
+1242 ... try:
+1243 ... await bus_task
+1244 ... except asyncio.exceptions.CancelledError:
+1245 ... pass
+1246 >>> asyncio.run(main())
+1247 """
+1248 background_tasks = set ()
+1249 while True :
+1250 message = await self . _queue . get ()
+1251 if "target" in message and message [ "target" ] == "" and "command" in message :
+1252 if message [ "command" ] == "get clients" :
+1253 for client in self . _plugins :
+1254 answer = Message ( "" )
+1255 answer [ "client" ] = client
+1256 answer [ "plugin" ] = self . _plugins [ client ]
+1257 answer [ "sends" ] = self . _send_reg . get_templates ( client )
+1258 answer [ "receives" ] = self . _recv_reg . get_templates ( client )
+1259 await self . _queue . put ( answer )
+1260 elif message [ "command" ] == "push conf" :
+1261 conf = {}
+1262 try :
+1263 with open ( sys . argv [ 1 ]) as conf_file :
+1264 conf = json . load ( conf_file )
+1265 except (
+1266 IndexError ,
+1267 FileNotFoundError ,
+1268 json . decoder . JSONDecodeError ,
+1269 ):
+1270 pass
+1271 if conf == message [ "conf" ]:
+1272 await self . _queue . put ( Message ( "" , { "event" : "conf unchanged" }))
+1273 else :
+1274 await self . _queue . put ( Message ( "" , { "event" : "conf changed" }))
+1275 with open ( sys . argv [ 1 ], "w" ) as conf_file :
+1276 json . dump ( message [ "conf" ], conf_file )
+1277 for callback in self . _recv_reg . get_callbacks ( message ):
+1278 task = asyncio . create_task ( callback ( message ))
+1279 background_tasks . add ( task )
+1280 task . add_done_callback ( background_tasks . discard )
+1281 self . _queue . task_done ()
+
+
+
+
Run the message bus forever.
+
+
+
>>> async def main ():
+... bus = MessageBus ()
+... bus_task = asyncio . create_task ( bus . run ())
+... bus_task . cancel ()
+... try :
+... await bus_task
+... except asyncio . exceptions . CancelledError :
+... pass
+>>> asyncio . run ( main ())
+
+
+
+
+
+
+
+
+
+
+
async def
+
send (self , message : Message ) -> None :
+
+
View Source
+
+
+
+
1283 async def send ( self , message : Message ) -> None :
+1284 """Send a message to the message bus.
+1285
+1286 >>> async def callback(message):
+1287 ... print(f"Got: {message}")
+1288 >>> async def main():
+1289 ... bus = MessageBus()
+1290 ... bus.register('Client 1', 'Test Plugin',
+1291 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1292 ... [([MessageTemplate({'target':
+1293 ... {'const': 'Client 1'}})],
+1294 ... callback)])
+1295 ... bus.register('Client 2', 'Test Plugin',
+1296 ... [MessageTemplate({})],
+1297 ... [([MessageTemplate({'target':
+1298 ... {'const': 'Client 2'}})],
+1299 ... callback)])
+1300 ... bus_task = asyncio.create_task(bus.run())
+1301 ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
+1302 ... 'k1': 'Test'})
+1303 ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
+1304 ... try:
+1305 ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
+1306 ... 'k1': 42})
+1307 ... except BusException as e:
+1308 ... print(e)
+1309 ... await asyncio.sleep(0)
+1310 ... bus_task.cancel()
+1311 ... try:
+1312 ... await bus_task
+1313 ... except asyncio.exceptions.CancelledError:
+1314 ... pass
+1315 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+1316 Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
+1317 not allowed for sender 'Client 1'.
+1318 Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
+1319 Got: {'sender': 'Client 2', 'target': 'Client 1'}
+1320 """
+1321 assert isinstance ( message [ "sender" ], str )
+1322 sender = message [ "sender" ]
+1323 if sender :
+1324 if not self . _send_reg . check ( sender , message ):
+1325 raise BusException (
+1326 f "Message ' { message } ' not allowed for sender ' { sender } '."
+1327 )
+1328 await self . _queue . put ( message )
+
+
+
+
Send a message to the message bus.
+
+
+
>>> async def callback ( message ):
+... print ( f "Got: { message } " )
+>>> async def main ():
+... bus = MessageBus ()
+... bus . register ( 'Client 1' , 'Test Plugin' ,
+... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})],
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 1' }})],
+... callback )])
+... bus . register ( 'Client 2' , 'Test Plugin' ,
+... [ MessageTemplate ({})],
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 2' }})],
+... callback )])
+... bus_task = asyncio . create_task ( bus . run ())
+... await bus . send ({ 'sender' : 'Client 1' , 'target' : 'Client 2' ,
+... 'k1' : 'Test' })
+... await bus . send ({ 'sender' : 'Client 2' , 'target' : 'Client 1' })
+... try :
+... await bus . send ({ 'sender' : 'Client 1' , 'target' : 'Client 2' ,
+... 'k1' : 42 })
+... except BusException as e :
+... print ( e )
+... await asyncio . sleep ( 0 )
+... bus_task . cancel ()
+... try :
+... await bus_task
+... except asyncio . exceptions . CancelledError :
+... pass
+>>> asyncio . run ( main ()) # doctest: +NORMALIZE_WHITESPACE
+Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
+not allowed for sender 'Client 1'.
+Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
+Got: {'sender': 'Client 2', 'target': 'Client 1'}
+
+
+
+
+
+
+
+
+
+
+
def
+
send_nowait (self , message : Message ) -> None :
+
+
View Source
+
+
+
+
1330 def send_nowait ( self , message : Message ) -> None :
+1331 """Send a message to the message bus without blocking.
+1332
+1333 >>> async def callback(message):
+1334 ... print(f"Got: {message}")
+1335 >>> async def main():
+1336 ... bus = MessageBus()
+1337 ... bus.register('Client 1', 'Test Plugin',
+1338 ... [MessageTemplate({'k1': {'type': 'string'}})],
+1339 ... [([MessageTemplate({'target':
+1340 ... {'const': 'Client 1'}})],
+1341 ... callback)])
+1342 ... bus.register('Client 2', 'Test Plugin',
+1343 ... [MessageTemplate({})],
+1344 ... [([MessageTemplate({'target':
+1345 ... {'const': 'Client 2'}})],
+1346 ... callback)])
+1347 ... bus_task = asyncio.create_task(bus.run())
+1348 ... bus.send_nowait({'sender': 'Client 1', 'target': 'Client 2',
+1349 ... 'k1': 'Test'})
+1350 ... bus.send_nowait({'sender': 'Client 2', 'target': 'Client 1'})
+1351 ... try:
+1352 ... bus.send_nowait({'sender': 'Client 1',
+1353 ... 'target': 'Client 2', 'k1': 42})
+1354 ... except BusException as e:
+1355 ... print(e)
+1356 ... await asyncio.sleep(0)
+1357 ... bus_task.cancel()
+1358 ... try:
+1359 ... await bus_task
+1360 ... except asyncio.exceptions.CancelledError:
+1361 ... pass
+1362 >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
+1363 Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
+1364 not allowed for sender 'Client 1'.
+1365 Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
+1366 Got: {'sender': 'Client 2', 'target': 'Client 1'}
+1367 """
+1368 assert isinstance ( message [ "sender" ], str )
+1369 sender = message [ "sender" ]
+1370 if sender :
+1371 if not self . _send_reg . check ( sender , message ):
+1372 raise BusException (
+1373 f "Message ' { message } ' not allowed for sender ' { sender } '."
+1374 )
+1375 self . _queue . put_nowait ( message )
+
+
+
+
Send a message to the message bus without blocking.
+
+
+
>>> async def callback ( message ):
+... print ( f "Got: { message } " )
+>>> async def main ():
+... bus = MessageBus ()
+... bus . register ( 'Client 1' , 'Test Plugin' ,
+... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})],
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 1' }})],
+... callback )])
+... bus . register ( 'Client 2' , 'Test Plugin' ,
+... [ MessageTemplate ({})],
+... [([ MessageTemplate ({ 'target' :
+... { 'const' : 'Client 2' }})],
+... callback )])
+... bus_task = asyncio . create_task ( bus . run ())
+... bus . send_nowait ({ 'sender' : 'Client 1' , 'target' : 'Client 2' ,
+... 'k1' : 'Test' })
+... bus . send_nowait ({ 'sender' : 'Client 2' , 'target' : 'Client 1' })
+... try :
+... bus . send_nowait ({ 'sender' : 'Client 1' ,
+... 'target' : 'Client 2' , 'k1' : 42 })
+... except BusException as e :
+... print ( e )
+... await asyncio . sleep ( 0 )
+... bus_task . cancel ()
+... try :
+... await bus_task
+... except asyncio . exceptions . CancelledError :
+... pass
+>>> asyncio . run ( main ()) # doctest: +NORMALIZE_WHITESPACE
+Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
+not allowed for sender 'Client 1'.
+Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
+Got: {'sender': 'Client 2', 'target': 'Client 1'}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/controlpi/pluginregistry.html b/doc/api/controlpi/pluginregistry.html
new file mode 100644
index 0000000..c073270
--- /dev/null
+++ b/doc/api/controlpi/pluginregistry.html
@@ -0,0 +1,684 @@
+
+
+
+
+
+
+ controlpi.pluginregistry API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Provide a generic plugin system.
+
+
The class PluginRegistry is initialised with the name of a namespace
+package and a base class.
+
+
All modules in the namespace package are loaded. These modules can be
+included in different distribution packages, which allows to dynamically
+add plugins to the system without changing any code.
+
+
Afterwards, all (direct and indirect) subclasses of the base class are
+registered as plugins under their class name. Class names should be unique,
+which cannot be programmatically enforced.
+
+
+
>>> class BasePlugin :
+... pass
+>>> class Plugin1 ( BasePlugin ):
+... pass
+>>> class Plugin2 ( BasePlugin ):
+... pass
+>>> registry = PluginRegistry ( 'importlib' , BasePlugin )
+
+
+
+
The registry provides a generic mapping interface with the class names as
+keys and the classes as values.
+
+
+
>>> print ( len ( registry ))
+2
+>>> for name in registry :
+... print ( f " { name } : { registry [ name ] } " )
+Plugin1: <class 'pluginregistry.Plugin1'>
+Plugin2: <class 'pluginregistry.Plugin2'>
+>>> if 'Plugin1' in registry :
+... print ( f "'Plugin1' is in registry." )
+'Plugin1' is in registry.
+>>> p1 = registry [ 'Plugin1' ]
+>>> i1 = p1 ()
+
+
+
+
+
+
+ View Source
+
+ 1 """Provide a generic plugin system.
+ 2
+ 3 The class PluginRegistry is initialised with the name of a namespace
+ 4 package and a base class.
+ 5
+ 6 All modules in the namespace package are loaded. These modules can be
+ 7 included in different distribution packages, which allows to dynamically
+ 8 add plugins to the system without changing any code.
+ 9
+ 10 Afterwards, all (direct and indirect) subclasses of the base class are
+ 11 registered as plugins under their class name. Class names should be unique,
+ 12 which cannot be programmatically enforced.
+ 13
+ 14 >>> class BasePlugin:
+ 15 ... pass
+ 16 >>> class Plugin1(BasePlugin):
+ 17 ... pass
+ 18 >>> class Plugin2(BasePlugin):
+ 19 ... pass
+ 20 >>> registry = PluginRegistry('importlib', BasePlugin)
+ 21
+ 22 The registry provides a generic mapping interface with the class names as
+ 23 keys and the classes as values.
+ 24
+ 25 >>> print(len(registry))
+ 26 2
+ 27 >>> for name in registry:
+ 28 ... print(f"{name}: {registry[name]}")
+ 29 Plugin1: <class 'pluginregistry.Plugin1'>
+ 30 Plugin2: <class 'pluginregistry.Plugin2'>
+ 31 >>> if 'Plugin1' in registry:
+ 32 ... print(f"'Plugin1' is in registry.")
+ 33 'Plugin1' is in registry.
+ 34 >>> p1 = registry['Plugin1']
+ 35 >>> i1 = p1()
+ 36 """
+ 37
+ 38 import importlib
+ 39 import pkgutil
+ 40 import collections.abc
+ 41
+ 42
+ 43 class PluginRegistry ( collections . abc . Mapping ):
+ 44 """Provide a registry for plugins.
+ 45
+ 46 Initialise the registry by loading all modules in the given namespace
+ 47 package and then registering all subclasses of the given base class as
+ 48 plugins (only simulated here â the code for Plugin1 and Plugin2 should
+ 49 be in modules in the given namespace package in real applications):
+ 50 >>> class BasePlugin:
+ 51 ... pass
+ 52 >>> class Plugin1(BasePlugin):
+ 53 ... pass
+ 54 >>> class Plugin2(BasePlugin):
+ 55 ... pass
+ 56 >>> registry = PluginRegistry('importlib', BasePlugin)
+ 57
+ 58 After initialisation, provide a mapping interface to the plugins:
+ 59 >>> print(len(registry))
+ 60 2
+ 61 >>> for name in registry:
+ 62 ... print(f"{name}: {registry[name]}")
+ 63 Plugin1: <class 'pluginregistry.Plugin1'>
+ 64 Plugin2: <class 'pluginregistry.Plugin2'>
+ 65 >>> if 'Plugin1' in registry:
+ 66 ... print(f"'Plugin1' is in registry.")
+ 67 'Plugin1' is in registry.
+ 68 """
+ 69
+ 70 def __init__ ( self , namespace_package : str , base_class : type ) -> None :
+ 71 """Initialise registry.
+ 72
+ 73 Import all modules defined in the given namespace package (in any
+ 74 distribution package currently installed in the path). Then register
+ 75 all subclasses of the given base class as plugins.
+ 76
+ 77 >>> class BasePlugin:
+ 78 ... pass
+ 79 >>> class Plugin1(BasePlugin):
+ 80 ... pass
+ 81 >>> class Plugin2(BasePlugin):
+ 82 ... pass
+ 83 >>> registry = PluginRegistry('importlib', BasePlugin)
+ 84 >>> for name in registry._plugins:
+ 85 ... print(f"{name}: {registry._plugins[name]}")
+ 86 Plugin1: <class 'pluginregistry.Plugin1'>
+ 87 Plugin2: <class 'pluginregistry.Plugin2'>
+ 88 """
+ 89 ns_mod = importlib . import_module ( namespace_package )
+ 90 ns_path = ns_mod . __path__
+ 91 ns_name = ns_mod . __name__
+ 92 for _ , mod_name , _ in pkgutil . iter_modules ( ns_path ):
+ 93 importlib . import_module ( f " { ns_name } . { mod_name } " )
+ 94
+ 95 def all_subclasses ( cls ):
+ 96 result = []
+ 97 for subcls in cls . __subclasses__ ():
+ 98 result . append ( subcls )
+ 99 result . extend ( all_subclasses ( subcls ))
+100 return result
+101
+102 self . _plugins = { cls . __name__ : cls for cls in all_subclasses ( base_class )}
+103
+104 def __len__ ( self ) -> int :
+105 """Get number of registered plugins.
+106
+107 >>> class BasePlugin:
+108 ... pass
+109 >>> class Plugin1(BasePlugin):
+110 ... pass
+111 >>> class Plugin2(BasePlugin):
+112 ... pass
+113 >>> registry = PluginRegistry('importlib', BasePlugin)
+114 >>> print(registry.__len__())
+115 2
+116 """
+117 return len ( self . _plugins )
+118
+119 def __iter__ ( self ):
+120 """Get an iterator of the registered plugins.
+121
+122 >>> class BasePlugin:
+123 ... pass
+124 >>> class Plugin1(BasePlugin):
+125 ... pass
+126 >>> class Plugin2(BasePlugin):
+127 ... pass
+128 >>> registry = PluginRegistry('importlib', BasePlugin)
+129 >>> print(type(registry.__iter__()))
+130 <class 'dict_keyiterator'>
+131 >>> for name in registry:
+132 ... print(name)
+133 Plugin1
+134 Plugin2
+135 """
+136 return iter ( self . _plugins )
+137
+138 def __getitem__ ( self , plugin_name : str ) -> type :
+139 """Get a registered plugin given its name.
+140
+141 >>> class BasePlugin:
+142 ... pass
+143 >>> class Plugin1(BasePlugin):
+144 ... pass
+145 >>> class Plugin2(BasePlugin):
+146 ... pass
+147 >>> registry = PluginRegistry('importlib', BasePlugin)
+148 >>> print(registry.__getitem__('Plugin1'))
+149 <class 'pluginregistry.Plugin1'>
+150 >>> print(registry.__getitem__('Plugin2'))
+151 <class 'pluginregistry.Plugin2'>
+152 >>> for name in registry:
+153 ... print(registry[name])
+154 <class 'pluginregistry.Plugin1'>
+155 <class 'pluginregistry.Plugin2'>
+156 """
+157 return self . _plugins [ plugin_name ]
+
+
+
+
+
+
+
+
+ class
+ PluginRegistry (collections.abc.Mapping ):
+
+ View Source
+
+
+
+ 44 class PluginRegistry ( collections . abc . Mapping ):
+ 45 """Provide a registry for plugins.
+ 46
+ 47 Initialise the registry by loading all modules in the given namespace
+ 48 package and then registering all subclasses of the given base class as
+ 49 plugins (only simulated here â the code for Plugin1 and Plugin2 should
+ 50 be in modules in the given namespace package in real applications):
+ 51 >>> class BasePlugin:
+ 52 ... pass
+ 53 >>> class Plugin1(BasePlugin):
+ 54 ... pass
+ 55 >>> class Plugin2(BasePlugin):
+ 56 ... pass
+ 57 >>> registry = PluginRegistry('importlib', BasePlugin)
+ 58
+ 59 After initialisation, provide a mapping interface to the plugins:
+ 60 >>> print(len(registry))
+ 61 2
+ 62 >>> for name in registry:
+ 63 ... print(f"{name}: {registry[name]}")
+ 64 Plugin1: <class 'pluginregistry.Plugin1'>
+ 65 Plugin2: <class 'pluginregistry.Plugin2'>
+ 66 >>> if 'Plugin1' in registry:
+ 67 ... print(f"'Plugin1' is in registry.")
+ 68 'Plugin1' is in registry.
+ 69 """
+ 70
+ 71 def __init__ ( self , namespace_package : str , base_class : type ) -> None :
+ 72 """Initialise registry.
+ 73
+ 74 Import all modules defined in the given namespace package (in any
+ 75 distribution package currently installed in the path). Then register
+ 76 all subclasses of the given base class as plugins.
+ 77
+ 78 >>> class BasePlugin:
+ 79 ... pass
+ 80 >>> class Plugin1(BasePlugin):
+ 81 ... pass
+ 82 >>> class Plugin2(BasePlugin):
+ 83 ... pass
+ 84 >>> registry = PluginRegistry('importlib', BasePlugin)
+ 85 >>> for name in registry._plugins:
+ 86 ... print(f"{name}: {registry._plugins[name]}")
+ 87 Plugin1: <class 'pluginregistry.Plugin1'>
+ 88 Plugin2: <class 'pluginregistry.Plugin2'>
+ 89 """
+ 90 ns_mod = importlib . import_module ( namespace_package )
+ 91 ns_path = ns_mod . __path__
+ 92 ns_name = ns_mod . __name__
+ 93 for _ , mod_name , _ in pkgutil . iter_modules ( ns_path ):
+ 94 importlib . import_module ( f " { ns_name } . { mod_name } " )
+ 95
+ 96 def all_subclasses ( cls ):
+ 97 result = []
+ 98 for subcls in cls . __subclasses__ ():
+ 99 result . append ( subcls )
+100 result . extend ( all_subclasses ( subcls ))
+101 return result
+102
+103 self . _plugins = { cls . __name__ : cls for cls in all_subclasses ( base_class )}
+104
+105 def __len__ ( self ) -> int :
+106 """Get number of registered plugins.
+107
+108 >>> class BasePlugin:
+109 ... pass
+110 >>> class Plugin1(BasePlugin):
+111 ... pass
+112 >>> class Plugin2(BasePlugin):
+113 ... pass
+114 >>> registry = PluginRegistry('importlib', BasePlugin)
+115 >>> print(registry.__len__())
+116 2
+117 """
+118 return len ( self . _plugins )
+119
+120 def __iter__ ( self ):
+121 """Get an iterator of the registered plugins.
+122
+123 >>> class BasePlugin:
+124 ... pass
+125 >>> class Plugin1(BasePlugin):
+126 ... pass
+127 >>> class Plugin2(BasePlugin):
+128 ... pass
+129 >>> registry = PluginRegistry('importlib', BasePlugin)
+130 >>> print(type(registry.__iter__()))
+131 <class 'dict_keyiterator'>
+132 >>> for name in registry:
+133 ... print(name)
+134 Plugin1
+135 Plugin2
+136 """
+137 return iter ( self . _plugins )
+138
+139 def __getitem__ ( self , plugin_name : str ) -> type :
+140 """Get a registered plugin given its name.
+141
+142 >>> class BasePlugin:
+143 ... pass
+144 >>> class Plugin1(BasePlugin):
+145 ... pass
+146 >>> class Plugin2(BasePlugin):
+147 ... pass
+148 >>> registry = PluginRegistry('importlib', BasePlugin)
+149 >>> print(registry.__getitem__('Plugin1'))
+150 <class 'pluginregistry.Plugin1'>
+151 >>> print(registry.__getitem__('Plugin2'))
+152 <class 'pluginregistry.Plugin2'>
+153 >>> for name in registry:
+154 ... print(registry[name])
+155 <class 'pluginregistry.Plugin1'>
+156 <class 'pluginregistry.Plugin2'>
+157 """
+158 return self . _plugins [ plugin_name ]
+
+
+
+ Provide a registry for plugins.
+
+
Initialise the registry by loading all modules in the given namespace
+package and then registering all subclasses of the given base class as
+plugins (only simulated here â the code for Plugin1 and Plugin2 should
+be in modules in the given namespace package in real applications):
+
+
+
>>> class BasePlugin :
+... pass
+>>> class Plugin1 ( BasePlugin ):
+... pass
+>>> class Plugin2 ( BasePlugin ):
+... pass
+>>> registry = PluginRegistry ( 'importlib' , BasePlugin )
+
+
+
+
After initialisation, provide a mapping interface to the plugins:
+
+
+
>>> print ( len ( registry ))
+2
+>>> for name in registry :
+... print ( f " { name } : { registry [ name ] } " )
+Plugin1: <class 'pluginregistry.Plugin1'>
+Plugin2: <class 'pluginregistry.Plugin2'>
+>>> if 'Plugin1' in registry :
+... print ( f "'Plugin1' is in registry." )
+'Plugin1' is in registry.
+
+
+
+
+
+
+
+
+
+ PluginRegistry (namespace_package : str , base_class : type )
+
+ View Source
+
+
+
+
71 def __init__ ( self , namespace_package : str , base_class : type ) -> None :
+ 72 """Initialise registry.
+ 73
+ 74 Import all modules defined in the given namespace package (in any
+ 75 distribution package currently installed in the path). Then register
+ 76 all subclasses of the given base class as plugins.
+ 77
+ 78 >>> class BasePlugin:
+ 79 ... pass
+ 80 >>> class Plugin1(BasePlugin):
+ 81 ... pass
+ 82 >>> class Plugin2(BasePlugin):
+ 83 ... pass
+ 84 >>> registry = PluginRegistry('importlib', BasePlugin)
+ 85 >>> for name in registry._plugins:
+ 86 ... print(f"{name}: {registry._plugins[name]}")
+ 87 Plugin1: <class 'pluginregistry.Plugin1'>
+ 88 Plugin2: <class 'pluginregistry.Plugin2'>
+ 89 """
+ 90 ns_mod = importlib . import_module ( namespace_package )
+ 91 ns_path = ns_mod . __path__
+ 92 ns_name = ns_mod . __name__
+ 93 for _ , mod_name , _ in pkgutil . iter_modules ( ns_path ):
+ 94 importlib . import_module ( f " { ns_name } . { mod_name } " )
+ 95
+ 96 def all_subclasses ( cls ):
+ 97 result = []
+ 98 for subcls in cls . __subclasses__ ():
+ 99 result . append ( subcls )
+100 result . extend ( all_subclasses ( subcls ))
+101 return result
+102
+103 self . _plugins = { cls . __name__ : cls for cls in all_subclasses ( base_class )}
+
+
+
+
Initialise registry.
+
+
Import all modules defined in the given namespace package (in any
+distribution package currently installed in the path). Then register
+all subclasses of the given base class as plugins.
+
+
+
>>> class BasePlugin :
+... pass
+>>> class Plugin1 ( BasePlugin ):
+... pass
+>>> class Plugin2 ( BasePlugin ):
+... pass
+>>> registry = PluginRegistry ( 'importlib' , BasePlugin )
+>>> for name in registry . _plugins :
+... print ( f " { name } : { registry . _plugins [ name ] } " )
+Plugin1: <class 'pluginregistry.Plugin1'>
+Plugin2: <class 'pluginregistry.Plugin2'>
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/controlpi_plugins.html b/doc/api/controlpi_plugins.html
new file mode 100644
index 0000000..6e2e933
--- /dev/null
+++ b/doc/api/controlpi_plugins.html
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+ controlpi_plugins API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+controlpi_plugins
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/controlpi_plugins/state.html b/doc/api/controlpi_plugins/state.html
new file mode 100644
index 0000000..9ffcff8
--- /dev/null
+++ b/doc/api/controlpi_plugins/state.html
@@ -0,0 +1,3256 @@
+
+
+
+
+
+
+ controlpi_plugins.state API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Provide state plugins for all kinds of systems.
+
+
+State represents a Boolean state.
+StateAlias translates to another state-like client.
+AndState combines several state-like clients by conjunction.
+OrState combines several state-like clients by disjunction.
+AndSet sets a state due to a conjunction of other state-like clients.
+OrSet sets a state due to a disjunction of other state-like clients.
+
+
+
All these plugins use the following conventions:
+
+
+If their state changes they send a message containing "event": "changed"
+and "state": NEW STATE.
+If their state is reported due to a message, but did not change they send
+a message containing just "state": CURRENT STATE.
+If they receive a message containing "target": NAME and
+"command": "get state" they report their current state.
+If State (or any other settable state using these conventions) receives
+a message containing "target": NAME, "command": "set state" and
+"new state": STATE TO SET it changes the state accordingly. If this
+was really a change the corresponding event is sent. If it was already in
+this state a report message without "event": "changed" is sent.
+StateAlias can alias any message bus client using these conventions, not
+just State instances. It translates all messages described here in both
+directions.
+AndState and OrState instances cannot be set.
+AndState and OrState can combine any message bus clients using these
+conventions, not just State instances. They only react to messages
+containing "state" information.
+
+
+
+
>>> import asyncio
+>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test State" : { "plugin" : "State" },
+... "Test State 2" : { "plugin" : "State" },
+... "Test State 3" : { "plugin" : "State" },
+... "Test State 4" : { "plugin" : "State" },
+... "Test StateAlias" : { "plugin" : "StateAlias" ,
+... "alias for" : "Test State 2" },
+... "Test AndState" : { "plugin" : "AndState" ,
+... "states" : [ "Test State" , "Test StateAlias" ]},
+... "Test OrState" : { "plugin" : "OrState" ,
+... "states" : [ "Test State" , "Test StateAlias" ]},
+... "Test AndSet" : { "plugin" : "AndSet" ,
+... "input states" : [ "Test State" , "Test StateAlias" ],
+... "output state" : "Test State 3" },
+... "Test OrSet" : { "plugin" : "OrSet" ,
+... "input states" : [ "Test State" , "Test StateAlias" ],
+... "output state" : "Test State 4" }},
+... [{ "target" : "Test AndState" ,
+... "command" : "get state" },
+... { "target" : "Test OrState" ,
+... "command" : "get state" },
+... { "target" : "Test State" ,
+... "command" : "set state" , "new state" : True },
+... { "target" : "Test StateAlias" ,
+... "command" : "set state" , "new state" : True },
+... { "target" : "Test State" ,
+... "command" : "set state" , "new state" : False }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered', ...
+test(): {'sender': 'test()', 'target': 'Test AndState', 'command': 'get state'}
+test(): {'sender': 'test()', 'target': 'Test OrState', 'command': 'get state'}
+test(): {'sender': 'Test AndState', 'state': False}
+test(): {'sender': 'test()', 'target': 'Test State',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test OrState', 'state': False}
+test(): {'sender': 'test()', 'target': 'Test StateAlias',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test State',
+ 'command': 'set state', 'new state': False}
+test(): {'sender': 'Test StateAlias', 'target': 'Test State 2',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
+test(): {'sender': 'Test OrSet', 'target': 'Test State 4',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test State', 'event': 'changed', 'state': False}
+test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+test(): {'sender': 'Test State 4', 'event': 'changed', 'state': True}
+
+
+
+
+
+
+ View Source
+
+ 1 """Provide state plugins for all kinds of systems.
+ 2
+ 3 - State represents a Boolean state.
+ 4 - StateAlias translates to another state-like client.
+ 5 - AndState combines several state-like clients by conjunction.
+ 6 - OrState combines several state-like clients by disjunction.
+ 7 - AndSet sets a state due to a conjunction of other state-like clients.
+ 8 - OrSet sets a state due to a disjunction of other state-like clients.
+ 9
+ 10 All these plugins use the following conventions:
+ 11
+ 12 - If their state changes they send a message containing "event": "changed"
+ 13 and "state": NEW STATE.
+ 14 - If their state is reported due to a message, but did not change they send
+ 15 a message containing just "state": CURRENT STATE.
+ 16 - If they receive a message containing "target": NAME and
+ 17 "command": "get state" they report their current state.
+ 18 - If State (or any other settable state using these conventions) receives
+ 19 a message containing "target": NAME, "command": "set state" and
+ 20 "new state": STATE TO SET it changes the state accordingly. If this
+ 21 was really a change the corresponding event is sent. If it was already in
+ 22 this state a report message without "event": "changed" is sent.
+ 23 - StateAlias can alias any message bus client using these conventions, not
+ 24 just State instances. It translates all messages described here in both
+ 25 directions.
+ 26 - AndState and OrState instances cannot be set.
+ 27 - AndState and OrState can combine any message bus clients using these
+ 28 conventions, not just State instances. They only react to messages
+ 29 containing "state" information.
+ 30
+ 31 >>> import asyncio
+ 32 >>> import controlpi
+ 33 >>> asyncio.run(controlpi.test(
+ 34 ... {"Test State": {"plugin": "State"},
+ 35 ... "Test State 2": {"plugin": "State"},
+ 36 ... "Test State 3": {"plugin": "State"},
+ 37 ... "Test State 4": {"plugin": "State"},
+ 38 ... "Test StateAlias": {"plugin": "StateAlias",
+ 39 ... "alias for": "Test State 2"},
+ 40 ... "Test AndState": {"plugin": "AndState",
+ 41 ... "states": ["Test State", "Test StateAlias"]},
+ 42 ... "Test OrState": {"plugin": "OrState",
+ 43 ... "states": ["Test State", "Test StateAlias"]},
+ 44 ... "Test AndSet": {"plugin": "AndSet",
+ 45 ... "input states": ["Test State", "Test StateAlias"],
+ 46 ... "output state": "Test State 3"},
+ 47 ... "Test OrSet": {"plugin": "OrSet",
+ 48 ... "input states": ["Test State", "Test StateAlias"],
+ 49 ... "output state": "Test State 4"}},
+ 50 ... [{"target": "Test AndState",
+ 51 ... "command": "get state"},
+ 52 ... {"target": "Test OrState",
+ 53 ... "command": "get state"},
+ 54 ... {"target": "Test State",
+ 55 ... "command": "set state", "new state": True},
+ 56 ... {"target": "Test StateAlias",
+ 57 ... "command": "set state", "new state": True},
+ 58 ... {"target": "Test State",
+ 59 ... "command": "set state", "new state": False}]))
+ 60 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+ 61 test(): {'sender': '', 'event': 'registered', ...
+ 62 test(): {'sender': 'test()', 'target': 'Test AndState', 'command': 'get state'}
+ 63 test(): {'sender': 'test()', 'target': 'Test OrState', 'command': 'get state'}
+ 64 test(): {'sender': 'Test AndState', 'state': False}
+ 65 test(): {'sender': 'test()', 'target': 'Test State',
+ 66 'command': 'set state', 'new state': True}
+ 67 test(): {'sender': 'Test OrState', 'state': False}
+ 68 test(): {'sender': 'test()', 'target': 'Test StateAlias',
+ 69 'command': 'set state', 'new state': True}
+ 70 test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
+ 71 test(): {'sender': 'test()', 'target': 'Test State',
+ 72 'command': 'set state', 'new state': False}
+ 73 test(): {'sender': 'Test StateAlias', 'target': 'Test State 2',
+ 74 'command': 'set state', 'new state': True}
+ 75 test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
+ 76 test(): {'sender': 'Test OrSet', 'target': 'Test State 4',
+ 77 'command': 'set state', 'new state': True}
+ 78 test(): {'sender': 'Test State', 'event': 'changed', 'state': False}
+ 79 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+ 80 test(): {'sender': 'Test State 4', 'event': 'changed', 'state': True}
+ 81 """
+ 82
+ 83 from controlpi import BasePlugin , Message , MessageTemplate
+ 84
+ 85 from typing import Dict , List
+ 86
+ 87
+ 88 class State ( BasePlugin ):
+ 89 """Provide a Boolean state.
+ 90
+ 91 The state of a State plugin instance can be queried with the "get state"
+ 92 command and set with the "set state" command to the new state given by
+ 93 the "new state" key:
+ 94 >>> import asyncio
+ 95 >>> import controlpi
+ 96 >>> asyncio.run(controlpi.test(
+ 97 ... {"Test State": {"plugin": "State"}},
+ 98 ... [{"target": "Test State", "command": "get state"},
+ 99 ... {"target": "Test State", "command": "set state",
+100 ... "new state": True},
+101 ... {"target": "Test State", "command": "set state",
+102 ... "new state": True},
+103 ... {"target": "Test State", "command": "get state"}]))
+104 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+105 test(): {'sender': '', 'event': 'registered', ...
+106 test(): {'sender': 'test()', 'target': 'Test State',
+107 'command': 'get state'}
+108 test(): {'sender': 'test()', 'target': 'Test State',
+109 'command': 'set state', 'new state': True}
+110 test(): {'sender': 'Test State', 'state': False}
+111 test(): {'sender': 'test()', 'target': 'Test State',
+112 'command': 'set state', 'new state': True}
+113 test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
+114 test(): {'sender': 'test()', 'target': 'Test State',
+115 'command': 'get state'}
+116 test(): {'sender': 'Test State', 'state': True}
+117 test(): {'sender': 'Test State', 'state': True}
+118 """
+119
+120 CONF_SCHEMA = True
+121 """Schema for State plugin configuration.
+122
+123 There are no required or optional configuration keys.
+124 """
+125
+126 def process_conf ( self ) -> None :
+127 """Register plugin as bus client."""
+128 self . state : bool = False
+129 self . bus . register (
+130 self . name ,
+131 "State" ,
+132 [
+133 MessageTemplate (
+134 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+135 ),
+136 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+137 ],
+138 [
+139 (
+140 [
+141 MessageTemplate (
+142 {
+143 "target" : { "const" : self . name },
+144 "command" : { "const" : "get state" },
+145 }
+146 )
+147 ],
+148 self . _get_state ,
+149 ),
+150 (
+151 [
+152 MessageTemplate (
+153 {
+154 "target" : { "const" : self . name },
+155 "command" : { "const" : "set state" },
+156 "new state" : { "type" : "boolean" },
+157 }
+158 )
+159 ],
+160 self . _set_state ,
+161 ),
+162 ],
+163 )
+164
+165 async def _get_state ( self , message : Message ) -> None :
+166 await self . bus . send ( Message ( self . name , { "state" : self . state }))
+167
+168 async def _set_state ( self , message : Message ) -> None :
+169 if self . state != message [ "new state" ]:
+170 assert isinstance ( message [ "new state" ], bool )
+171 self . state = message [ "new state" ]
+172 await self . bus . send (
+173 Message ( self . name , { "event" : "changed" , "state" : self . state })
+174 )
+175 else :
+176 await self . bus . send ( Message ( self . name , { "state" : self . state }))
+177
+178 async def run ( self ) -> None :
+179 """Run no code proactively."""
+180 pass
+181
+182
+183 class StateAlias ( BasePlugin ):
+184 """Define an alias for another state.
+185
+186 The "alias for" configuration key gets the name for the other state that
+187 is aliased by the StateAlias plugin instance.
+188
+189 The "get state" and "set state" commands are forwarded to and the
+190 "changed" events and "state" messages are forwarded from this other
+191 state:
+192 >>> import asyncio
+193 >>> import controlpi
+194 >>> asyncio.run(controlpi.test(
+195 ... {"Test State": {"plugin": "State"},
+196 ... "Test StateAlias": {"plugin": "StateAlias",
+197 ... "alias for": "Test State"}},
+198 ... [{"target": "Test State", "command": "get state"},
+199 ... {"target": "Test StateAlias", "command": "set state",
+200 ... "new state": True},
+201 ... {"target": "Test State", "command": "set state",
+202 ... "new state": True},
+203 ... {"target": "Test StateAlias", "command": "get state"}]))
+204 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+205 test(): {'sender': '', 'event': 'registered', ...
+206 test(): {'sender': 'test()', 'target': 'Test State',
+207 'command': 'get state'}
+208 test(): {'sender': 'test()', 'target': 'Test StateAlias',
+209 'command': 'set state', 'new state': True}
+210 test(): {'sender': 'Test State', 'state': False}
+211 test(): {'sender': 'test()', 'target': 'Test State',
+212 'command': 'set state', 'new state': True}
+213 test(): {'sender': 'Test StateAlias', 'target': 'Test State',
+214 'command': 'set state', 'new state': True}
+215 test(): {'sender': 'Test StateAlias', 'state': False}
+216 test(): {'sender': 'test()', 'target': 'Test StateAlias',
+217 'command': 'get state'}
+218 test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
+219 test(): {'sender': 'Test State', 'state': True}
+220 test(): {'sender': 'Test StateAlias', 'target': 'Test State',
+221 'command': 'get state'}
+222 test(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True}
+223 test(): {'sender': 'Test StateAlias', 'state': True}
+224 """
+225
+226 CONF_SCHEMA = {
+227 "properties" : { "alias for" : { "type" : "string" }},
+228 "required" : [ "alias for" ],
+229 }
+230 """Schema for StateAlias plugin configuration.
+231
+232 Required configuration key:
+233
+234 - 'alias for': name of aliased state.
+235 """
+236
+237 def process_conf ( self ) -> None :
+238 """Register plugin as bus client."""
+239 self . bus . register (
+240 self . name ,
+241 "StateAlias" ,
+242 [
+243 MessageTemplate (
+244 {
+245 "target" : { "const" : self . conf [ "alias for" ]},
+246 "command" : { "const" : "get state" },
+247 }
+248 ),
+249 MessageTemplate (
+250 {
+251 "target" : { "const" : self . conf [ "alias for" ]},
+252 "command" : { "const" : "set state" },
+253 "new state" : { "type" : "boolean" },
+254 }
+255 ),
+256 MessageTemplate (
+257 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+258 ),
+259 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+260 ],
+261 [
+262 (
+263 [
+264 MessageTemplate (
+265 {
+266 "target" : { "const" : self . name },
+267 "command" : { "const" : "get state" },
+268 }
+269 )
+270 ],
+271 self . _get_state ,
+272 ),
+273 (
+274 [
+275 MessageTemplate (
+276 {
+277 "target" : { "const" : self . name },
+278 "command" : { "const" : "set state" },
+279 "new state" : { "type" : "boolean" },
+280 }
+281 )
+282 ],
+283 self . _set_state ,
+284 ),
+285 (
+286 [
+287 MessageTemplate (
+288 {
+289 "sender" : { "const" : self . conf [ "alias for" ]},
+290 "state" : { "type" : "boolean" },
+291 }
+292 )
+293 ],
+294 self . _translate ,
+295 ),
+296 ],
+297 )
+298
+299 async def _get_state ( self , message : Message ) -> None :
+300 await self . bus . send (
+301 Message (
+302 self . name , { "target" : self . conf [ "alias for" ], "command" : "get state" }
+303 )
+304 )
+305
+306 async def _set_state ( self , message : Message ) -> None :
+307 await self . bus . send (
+308 Message (
+309 self . name ,
+310 {
+311 "target" : self . conf [ "alias for" ],
+312 "command" : "set state" ,
+313 "new state" : message [ "new state" ],
+314 },
+315 )
+316 )
+317
+318 async def _translate ( self , message : Message ) -> None :
+319 alias_message = Message ( self . name )
+320 if "event" in message and message [ "event" ] == "changed" :
+321 alias_message [ "event" ] = "changed"
+322 alias_message [ "state" ] = message [ "state" ]
+323 await self . bus . send ( alias_message )
+324
+325 async def run ( self ) -> None :
+326 """Run no code proactively."""
+327 pass
+328
+329
+330 class AndState ( BasePlugin ):
+331 """Define conjunction of states.
+332
+333 The "states" configuration key gets an array of states to be combined.
+334 An AndState plugin client reacts to "get state" commands and sends
+335 "changed" events when a change in one of the combined states leads to
+336 a change for the conjunction:
+337 >>> import asyncio
+338 >>> import controlpi
+339 >>> asyncio.run(controlpi.test(
+340 ... {"Test State 1": {"plugin": "State"},
+341 ... "Test State 2": {"plugin": "State"},
+342 ... "Test AndState": {"plugin": "AndState",
+343 ... "states": ["Test State 1", "Test State 2"]}},
+344 ... [{"target": "Test State 1", "command": "set state",
+345 ... "new state": True},
+346 ... {"target": "Test State 2", "command": "set state",
+347 ... "new state": True},
+348 ... {"target": "Test State 1", "command": "set state",
+349 ... "new state": False},
+350 ... {"target": "Test AndState", "command": "get state"},
+351 ... {"target": "Test AndState", "command": "get sources"}]))
+352 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+353 test(): {'sender': '', 'event': 'registered', ...
+354 test(): {'sender': 'test()', 'target': 'Test State 1',
+355 'command': 'set state', 'new state': True}
+356 test(): {'sender': 'test()', 'target': 'Test State 2',
+357 'command': 'set state', 'new state': True}
+358 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+359 test(): {'sender': 'test()', 'target': 'Test State 1',
+360 'command': 'set state', 'new state': False}
+361 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+362 test(): {'sender': 'test()', 'target': 'Test AndState',
+363 'command': 'get state'}
+364 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+365 test(): {'sender': 'Test AndState', 'event': 'changed', 'state': True}
+366 test(): {'sender': 'test()', 'target': 'Test AndState',
+367 'command': 'get sources'}
+368 test(): {'sender': 'Test AndState', 'state': True}
+369 test(): {'sender': 'Test AndState', 'event': 'changed', 'state': False}
+370 test(): {'sender': 'Test AndState',
+371 'states': ['Test State 1', 'Test State 2']}
+372 """
+373
+374 CONF_SCHEMA = {
+375 "properties" : { "states" : { "type" : "array" , "items" : { "type" : "string" }}},
+376 "required" : [ "states" ],
+377 }
+378 """Schema for AndState plugin configuration.
+379
+380 Required configuration key:
+381
+382 - 'states': list of names of combined states.
+383 """
+384
+385 def process_conf ( self ) -> None :
+386 """Register plugin as bus client."""
+387 updates : List [ MessageTemplate ] = []
+388 self . states : Dict [ str , bool ] = {}
+389 for state in self . conf [ "states" ]:
+390 updates . append (
+391 MessageTemplate (
+392 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+393 )
+394 )
+395 self . states [ state ] = False
+396 self . state : bool = all ( self . states . values ())
+397 self . bus . register (
+398 self . name ,
+399 "AndState" ,
+400 [
+401 MessageTemplate (
+402 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+403 ),
+404 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+405 MessageTemplate (
+406 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+407 ),
+408 ],
+409 [
+410 (
+411 [
+412 MessageTemplate (
+413 {
+414 "target" : { "const" : self . name },
+415 "command" : { "const" : "get state" },
+416 }
+417 )
+418 ],
+419 self . _get_state ,
+420 ),
+421 (
+422 [
+423 MessageTemplate (
+424 {
+425 "target" : { "const" : self . name },
+426 "command" : { "const" : "get sources" },
+427 }
+428 )
+429 ],
+430 self . _get_sources ,
+431 ),
+432 ( updates , self . _update ),
+433 ],
+434 )
+435
+436 async def _get_state ( self , message : Message ) -> None :
+437 await self . bus . send ( Message ( self . name , { "state" : self . state }))
+438
+439 async def _get_sources ( self , message : Message ) -> None :
+440 source_states = list ( self . states . keys ())
+441 await self . bus . send ( Message ( self . name , { "states" : source_states }))
+442
+443 async def _update ( self , message : Message ) -> None :
+444 assert isinstance ( message [ "sender" ], str )
+445 assert isinstance ( message [ "state" ], bool )
+446 self . states [ message [ "sender" ]] = message [ "state" ]
+447 new_state = all ( self . states . values ())
+448 if self . state != new_state :
+449 self . state = new_state
+450 await self . bus . send (
+451 Message ( self . name , { "event" : "changed" , "state" : self . state })
+452 )
+453
+454 async def run ( self ) -> None :
+455 """Run no code proactively."""
+456 pass
+457
+458
+459 class OrState ( BasePlugin ):
+460 """Define disjunction of states.
+461
+462 The "states" configuration key gets an array of states to be combined.
+463 An OrState plugin client reacts to "get state" commands and sends
+464 "changed" events when a change in one of the combined states leads to
+465 a change for the disjunction:
+466 >>> import asyncio
+467 >>> import controlpi
+468 >>> asyncio.run(controlpi.test(
+469 ... {"Test State 1": {"plugin": "State"},
+470 ... "Test State 2": {"plugin": "State"},
+471 ... "Test OrState": {"plugin": "OrState",
+472 ... "states": ["Test State 1", "Test State 2"]}},
+473 ... [{"target": "Test State 1", "command": "set state",
+474 ... "new state": True},
+475 ... {"target": "Test State 2", "command": "set state",
+476 ... "new state": True},
+477 ... {"target": "Test State 1", "command": "set state",
+478 ... "new state": False},
+479 ... {"target": "Test OrState", "command": "get state"},
+480 ... {"target": "Test OrState", "command": "get sources"}]))
+481 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+482 test(): {'sender': '', 'event': 'registered', ...
+483 test(): {'sender': 'test()', 'target': 'Test State 1',
+484 'command': 'set state', 'new state': True}
+485 test(): {'sender': 'test()', 'target': 'Test State 2',
+486 'command': 'set state', 'new state': True}
+487 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+488 test(): {'sender': 'test()', 'target': 'Test State 1',
+489 'command': 'set state', 'new state': False}
+490 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+491 test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
+492 test(): {'sender': 'test()', 'target': 'Test OrState',
+493 'command': 'get state'}
+494 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+495 test(): {'sender': 'test()', 'target': 'Test OrState',
+496 'command': 'get sources'}
+497 test(): {'sender': 'Test OrState', 'state': True}
+498 test(): {'sender': 'Test OrState',
+499 'states': ['Test State 1', 'Test State 2']}
+500 """
+501
+502 CONF_SCHEMA = {
+503 "properties" : { "states" : { "type" : "array" , "items" : { "type" : "string" }}},
+504 "required" : [ "states" ],
+505 }
+506 """Schema for OrState plugin configuration.
+507
+508 Required configuration key:
+509
+510 - 'states': list of names of combined states.
+511 """
+512
+513 def process_conf ( self ) -> None :
+514 """Register plugin as bus client."""
+515 updates : List [ MessageTemplate ] = []
+516 self . states : Dict [ str , bool ] = {}
+517 for state in self . conf [ "states" ]:
+518 updates . append (
+519 MessageTemplate (
+520 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+521 )
+522 )
+523 self . states [ state ] = False
+524 self . state : bool = any ( self . states . values ())
+525 self . bus . register (
+526 self . name ,
+527 "OrState" ,
+528 [
+529 MessageTemplate (
+530 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+531 ),
+532 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+533 MessageTemplate (
+534 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+535 ),
+536 ],
+537 [
+538 (
+539 [
+540 MessageTemplate (
+541 {
+542 "target" : { "const" : self . name },
+543 "command" : { "const" : "get state" },
+544 }
+545 )
+546 ],
+547 self . _get_state ,
+548 ),
+549 (
+550 [
+551 MessageTemplate (
+552 {
+553 "target" : { "const" : self . name },
+554 "command" : { "const" : "get sources" },
+555 }
+556 )
+557 ],
+558 self . _get_sources ,
+559 ),
+560 ( updates , self . _update ),
+561 ],
+562 )
+563
+564 async def _get_state ( self , message : Message ) -> None :
+565 await self . bus . send ( Message ( self . name , { "state" : self . state }))
+566
+567 async def _get_sources ( self , message : Message ) -> None :
+568 source_states = list ( self . states . keys ())
+569 await self . bus . send ( Message ( self . name , { "states" : source_states }))
+570
+571 async def _update ( self , message : Message ) -> None :
+572 assert isinstance ( message [ "sender" ], str )
+573 assert isinstance ( message [ "state" ], bool )
+574 self . states [ message [ "sender" ]] = message [ "state" ]
+575 new_state = any ( self . states . values ())
+576 if self . state != new_state :
+577 self . state = new_state
+578 await self . bus . send (
+579 Message ( self . name , { "event" : "changed" , "state" : self . state })
+580 )
+581
+582 async def run ( self ) -> None :
+583 """Run no code proactively."""
+584 pass
+585
+586
+587 class AndSet ( BasePlugin ):
+588 """Set state based on conjunction of other states.
+589
+590 The "input states" configuration key gets an array of states used to
+591 determine the state in the "output state" configuration key:
+592 >>> import asyncio
+593 >>> import controlpi
+594 >>> asyncio.run(controlpi.test(
+595 ... {"Test State 1": {"plugin": "State"},
+596 ... "Test State 2": {"plugin": "State"},
+597 ... "Test State 3": {"plugin": "State"},
+598 ... "Test AndSet": {"plugin": "AndSet",
+599 ... "input states": ["Test State 1",
+600 ... "Test State 2"],
+601 ... "output state": "Test State 3"}},
+602 ... [{"target": "Test State 1", "command": "set state",
+603 ... "new state": True},
+604 ... {"target": "Test State 2", "command": "set state",
+605 ... "new state": True},
+606 ... {"target": "Test AndSet", "command": "get state"},
+607 ... {"target": "Test State 1", "command": "set state",
+608 ... "new state": False},
+609 ... {"target": "Test AndSet", "command": "get state"},
+610 ... {"target": "Test AndSet", "command": "get sources"}]))
+611 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+612 test(): {'sender': '', 'event': 'registered', ...
+613 test(): {'sender': 'test()', 'target': 'Test State 1',
+614 'command': 'set state', 'new state': True}
+615 test(): {'sender': 'test()', 'target': 'Test State 2',
+616 'command': 'set state', 'new state': True}
+617 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+618 test(): {'sender': 'test()', 'target': 'Test AndSet',
+619 'command': 'get state'}
+620 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+621 test(): {'sender': 'test()', 'target': 'Test State 1',
+622 'command': 'set state', 'new state': False}
+623 test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+624 'command': 'set state', 'new state': False}
+625 test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+626 'command': 'set state', 'new state': True}
+627 test(): {'sender': 'test()', 'target': 'Test AndSet',
+628 'command': 'get state'}
+629 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+630 test(): {'sender': 'Test State 3', 'state': False}
+631 test(): {'sender': 'Test State 3', 'event': 'changed', 'state': True}
+632 test(): {'sender': 'test()', 'target': 'Test AndSet',
+633 'command': 'get sources'}
+634 test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+635 'command': 'set state', 'new state': True}
+636 test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+637 'command': 'set state', 'new state': False}
+638 test(): {'sender': 'Test AndSet',
+639 'states': ['Test State 1', 'Test State 2']}
+640 test(): {'sender': 'Test State 3', 'state': True}
+641 test(): {'sender': 'Test State 3', 'event': 'changed', 'state': False}
+642 """
+643
+644 CONF_SCHEMA = {
+645 "properties" : {
+646 "input states" : { "type" : "array" , "items" : { "type" : "string" }},
+647 "output state" : { "type" : "string" },
+648 },
+649 "required" : [ "input states" , "output state" ],
+650 }
+651 """Schema for AndSet plugin configuration.
+652
+653 Required configuration keys:
+654
+655 - 'input states': list of names of combined states.
+656 - 'output state': name of state to be set.
+657 """
+658
+659 def process_conf ( self ) -> None :
+660 """Register plugin as bus client."""
+661 updates : List [ MessageTemplate ] = []
+662 self . states : Dict [ str , bool ] = {}
+663 for state in self . conf [ "input states" ]:
+664 updates . append (
+665 MessageTemplate (
+666 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+667 )
+668 )
+669 self . states [ state ] = False
+670 self . state : bool = all ( self . states . values ())
+671 self . bus . register (
+672 self . name ,
+673 "AndSet" ,
+674 [
+675 MessageTemplate (
+676 {
+677 "target" : { "const" : self . conf [ "output state" ]},
+678 "command" : { "const" : "set state" },
+679 "new state" : { "type" : "boolean" },
+680 }
+681 ),
+682 MessageTemplate (
+683 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+684 ),
+685 ],
+686 [
+687 (
+688 [
+689 MessageTemplate (
+690 {
+691 "target" : { "const" : self . name },
+692 "command" : { "const" : "get state" },
+693 }
+694 )
+695 ],
+696 self . _get_state ,
+697 ),
+698 (
+699 [
+700 MessageTemplate (
+701 {
+702 "target" : { "const" : self . name },
+703 "command" : { "const" : "get sources" },
+704 }
+705 )
+706 ],
+707 self . _get_sources ,
+708 ),
+709 ( updates , self . _update ),
+710 ],
+711 )
+712
+713 async def _get_state ( self , message : Message ) -> None :
+714 await self . bus . send (
+715 Message (
+716 self . name ,
+717 {
+718 "target" : self . conf [ "output state" ],
+719 "command" : "set state" ,
+720 "new state" : self . state ,
+721 },
+722 )
+723 )
+724
+725 async def _get_sources ( self , message : Message ) -> None :
+726 source_states = list ( self . states . keys ())
+727 await self . bus . send ( Message ( self . name , { "states" : source_states }))
+728
+729 async def _update ( self , message : Message ) -> None :
+730 assert isinstance ( message [ "sender" ], str )
+731 assert isinstance ( message [ "state" ], bool )
+732 self . states [ message [ "sender" ]] = message [ "state" ]
+733 new_state = all ( self . states . values ())
+734 if self . state != new_state :
+735 self . state = new_state
+736 await self . bus . send (
+737 Message (
+738 self . name ,
+739 {
+740 "target" : self . conf [ "output state" ],
+741 "command" : "set state" ,
+742 "new state" : self . state ,
+743 },
+744 )
+745 )
+746
+747 async def run ( self ) -> None :
+748 """Run no code proactively."""
+749 pass
+750
+751
+752 class OrSet ( BasePlugin ):
+753 """Set state based on disjunction of other states.
+754
+755 The "input states" configuration key gets an array of states used to
+756 determine the state in the "output state" configuration key:
+757 >>> import asyncio
+758 >>> import controlpi
+759 >>> asyncio.run(controlpi.test(
+760 ... {"Test State 1": {"plugin": "State"},
+761 ... "Test State 2": {"plugin": "State"},
+762 ... "Test State 3": {"plugin": "State"},
+763 ... "Test OrSet": {"plugin": "OrSet",
+764 ... "input states": ["Test State 1",
+765 ... "Test State 2"],
+766 ... "output state": "Test State 3"}},
+767 ... [{"target": "Test State 1", "command": "set state",
+768 ... "new state": True},
+769 ... {"target": "Test OrSet", "command": "get state"},
+770 ... {"target": "Test State 2", "command": "set state",
+771 ... "new state": True},
+772 ... {"target": "Test State 1", "command": "set state",
+773 ... "new state": False},
+774 ... {"target": "Test OrSet", "command": "get sources"}]))
+775 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+776 test(): {'sender': '', 'event': 'registered', ...
+777 test(): {'sender': 'test()', 'target': 'Test State 1',
+778 'command': 'set state', 'new state': True}
+779 test(): {'sender': 'test()', 'target': 'Test OrSet',
+780 'command': 'get state'}
+781 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+782 test(): {'sender': 'test()', 'target': 'Test State 2',
+783 'command': 'set state', 'new state': True}
+784 test(): {'sender': 'Test OrSet', 'target': 'Test State 3',
+785 'command': 'set state', 'new state': False}
+786 test(): {'sender': 'Test OrSet', 'target': 'Test State 3',
+787 'command': 'set state', 'new state': True}
+788 test(): {'sender': 'test()', 'target': 'Test State 1',
+789 'command': 'set state', 'new state': False}
+790 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+791 test(): {'sender': 'Test State 3', 'state': False}
+792 test(): {'sender': 'Test State 3', 'event': 'changed', 'state': True}
+793 test(): {'sender': 'test()', 'target': 'Test OrSet',
+794 'command': 'get sources'}
+795 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+796 test(): {'sender': 'Test OrSet',
+797 'states': ['Test State 1', 'Test State 2']}
+798 """
+799
+800 CONF_SCHEMA = {
+801 "properties" : {
+802 "input states" : { "type" : "array" , "items" : { "type" : "string" }},
+803 "output state" : { "type" : "string" },
+804 },
+805 "required" : [ "input states" , "output state" ],
+806 }
+807 """Schema for OrSet plugin configuration.
+808
+809 Required configuration keys:
+810
+811 - 'input states': list of names of combined states.
+812 - 'output state': name of state to be set.
+813 """
+814
+815 def process_conf ( self ) -> None :
+816 """Register plugin as bus client."""
+817 updates : List [ MessageTemplate ] = []
+818 self . states : Dict [ str , bool ] = {}
+819 for state in self . conf [ "input states" ]:
+820 updates . append (
+821 MessageTemplate (
+822 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+823 )
+824 )
+825 self . states [ state ] = False
+826 self . state : bool = any ( self . states . values ())
+827 self . bus . register (
+828 self . name ,
+829 "AndSet" ,
+830 [
+831 MessageTemplate (
+832 {
+833 "target" : { "const" : self . conf [ "output state" ]},
+834 "command" : { "const" : "set state" },
+835 "new state" : { "type" : "boolean" },
+836 }
+837 ),
+838 MessageTemplate (
+839 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+840 ),
+841 ],
+842 [
+843 (
+844 [
+845 MessageTemplate (
+846 {
+847 "target" : { "const" : self . name },
+848 "command" : { "const" : "get state" },
+849 }
+850 )
+851 ],
+852 self . _get_state ,
+853 ),
+854 (
+855 [
+856 MessageTemplate (
+857 {
+858 "target" : { "const" : self . name },
+859 "command" : { "const" : "get sources" },
+860 }
+861 )
+862 ],
+863 self . _get_sources ,
+864 ),
+865 ( updates , self . _update ),
+866 ],
+867 )
+868
+869 async def _get_state ( self , message : Message ) -> None :
+870 await self . bus . send (
+871 Message (
+872 self . name ,
+873 {
+874 "target" : self . conf [ "output state" ],
+875 "command" : "set state" ,
+876 "new state" : self . state ,
+877 },
+878 )
+879 )
+880
+881 async def _get_sources ( self , message : Message ) -> None :
+882 source_states = list ( self . states . keys ())
+883 await self . bus . send ( Message ( self . name , { "states" : source_states }))
+884
+885 async def _update ( self , message : Message ) -> None :
+886 assert isinstance ( message [ "sender" ], str )
+887 assert isinstance ( message [ "state" ], bool )
+888 self . states [ message [ "sender" ]] = message [ "state" ]
+889 new_state = any ( self . states . values ())
+890 if self . state != new_state :
+891 self . state = new_state
+892 await self . bus . send (
+893 Message (
+894 self . name ,
+895 {
+896 "target" : self . conf [ "output state" ],
+897 "command" : "set state" ,
+898 "new state" : self . state ,
+899 },
+900 )
+901 )
+902
+903 async def run ( self ) -> None :
+904 """Run no code proactively."""
+905 pass
+
+
+
+
+
+
+
+
+ 89 class State ( BasePlugin ):
+ 90 """Provide a Boolean state.
+ 91
+ 92 The state of a State plugin instance can be queried with the "get state"
+ 93 command and set with the "set state" command to the new state given by
+ 94 the "new state" key:
+ 95 >>> import asyncio
+ 96 >>> import controlpi
+ 97 >>> asyncio.run(controlpi.test(
+ 98 ... {"Test State": {"plugin": "State"}},
+ 99 ... [{"target": "Test State", "command": "get state"},
+100 ... {"target": "Test State", "command": "set state",
+101 ... "new state": True},
+102 ... {"target": "Test State", "command": "set state",
+103 ... "new state": True},
+104 ... {"target": "Test State", "command": "get state"}]))
+105 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+106 test(): {'sender': '', 'event': 'registered', ...
+107 test(): {'sender': 'test()', 'target': 'Test State',
+108 'command': 'get state'}
+109 test(): {'sender': 'test()', 'target': 'Test State',
+110 'command': 'set state', 'new state': True}
+111 test(): {'sender': 'Test State', 'state': False}
+112 test(): {'sender': 'test()', 'target': 'Test State',
+113 'command': 'set state', 'new state': True}
+114 test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
+115 test(): {'sender': 'test()', 'target': 'Test State',
+116 'command': 'get state'}
+117 test(): {'sender': 'Test State', 'state': True}
+118 test(): {'sender': 'Test State', 'state': True}
+119 """
+120
+121 CONF_SCHEMA = True
+122 """Schema for State plugin configuration.
+123
+124 There are no required or optional configuration keys.
+125 """
+126
+127 def process_conf ( self ) -> None :
+128 """Register plugin as bus client."""
+129 self . state : bool = False
+130 self . bus . register (
+131 self . name ,
+132 "State" ,
+133 [
+134 MessageTemplate (
+135 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+136 ),
+137 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+138 ],
+139 [
+140 (
+141 [
+142 MessageTemplate (
+143 {
+144 "target" : { "const" : self . name },
+145 "command" : { "const" : "get state" },
+146 }
+147 )
+148 ],
+149 self . _get_state ,
+150 ),
+151 (
+152 [
+153 MessageTemplate (
+154 {
+155 "target" : { "const" : self . name },
+156 "command" : { "const" : "set state" },
+157 "new state" : { "type" : "boolean" },
+158 }
+159 )
+160 ],
+161 self . _set_state ,
+162 ),
+163 ],
+164 )
+165
+166 async def _get_state ( self , message : Message ) -> None :
+167 await self . bus . send ( Message ( self . name , { "state" : self . state }))
+168
+169 async def _set_state ( self , message : Message ) -> None :
+170 if self . state != message [ "new state" ]:
+171 assert isinstance ( message [ "new state" ], bool )
+172 self . state = message [ "new state" ]
+173 await self . bus . send (
+174 Message ( self . name , { "event" : "changed" , "state" : self . state })
+175 )
+176 else :
+177 await self . bus . send ( Message ( self . name , { "state" : self . state }))
+178
+179 async def run ( self ) -> None :
+180 """Run no code proactively."""
+181 pass
+
+
+
+ Provide a Boolean state.
+
+
The state of a State plugin instance can be queried with the "get state"
+command and set with the "set state" command to the new state given by
+the "new state" key:
+
+
+
>>> import asyncio
+>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test State" : { "plugin" : "State" }},
+... [{ "target" : "Test State" , "command" : "get state" },
+... { "target" : "Test State" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State" , "command" : "get state" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered', ...
+test(): {'sender': 'test()', 'target': 'Test State',
+ 'command': 'get state'}
+test(): {'sender': 'test()', 'target': 'Test State',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test State', 'state': False}
+test(): {'sender': 'test()', 'target': 'Test State',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test State',
+ 'command': 'get state'}
+test(): {'sender': 'Test State', 'state': True}
+test(): {'sender': 'Test State', 'state': True}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+True
+
+
+
+
+
+
Schema for State plugin configuration.
+
+
There are no required or optional configuration keys.
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
127 def process_conf ( self ) -> None :
+128 """Register plugin as bus client."""
+129 self . state : bool = False
+130 self . bus . register (
+131 self . name ,
+132 "State" ,
+133 [
+134 MessageTemplate (
+135 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+136 ),
+137 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+138 ],
+139 [
+140 (
+141 [
+142 MessageTemplate (
+143 {
+144 "target" : { "const" : self . name },
+145 "command" : { "const" : "get state" },
+146 }
+147 )
+148 ],
+149 self . _get_state ,
+150 ),
+151 (
+152 [
+153 MessageTemplate (
+154 {
+155 "target" : { "const" : self . name },
+156 "command" : { "const" : "set state" },
+157 "new state" : { "type" : "boolean" },
+158 }
+159 )
+160 ],
+161 self . _set_state ,
+162 ),
+163 ],
+164 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
179 async def run ( self ) -> None :
+180 """Run no code proactively."""
+181 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 184 class StateAlias ( BasePlugin ):
+185 """Define an alias for another state.
+186
+187 The "alias for" configuration key gets the name for the other state that
+188 is aliased by the StateAlias plugin instance.
+189
+190 The "get state" and "set state" commands are forwarded to and the
+191 "changed" events and "state" messages are forwarded from this other
+192 state:
+193 >>> import asyncio
+194 >>> import controlpi
+195 >>> asyncio.run(controlpi.test(
+196 ... {"Test State": {"plugin": "State"},
+197 ... "Test StateAlias": {"plugin": "StateAlias",
+198 ... "alias for": "Test State"}},
+199 ... [{"target": "Test State", "command": "get state"},
+200 ... {"target": "Test StateAlias", "command": "set state",
+201 ... "new state": True},
+202 ... {"target": "Test State", "command": "set state",
+203 ... "new state": True},
+204 ... {"target": "Test StateAlias", "command": "get state"}]))
+205 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+206 test(): {'sender': '', 'event': 'registered', ...
+207 test(): {'sender': 'test()', 'target': 'Test State',
+208 'command': 'get state'}
+209 test(): {'sender': 'test()', 'target': 'Test StateAlias',
+210 'command': 'set state', 'new state': True}
+211 test(): {'sender': 'Test State', 'state': False}
+212 test(): {'sender': 'test()', 'target': 'Test State',
+213 'command': 'set state', 'new state': True}
+214 test(): {'sender': 'Test StateAlias', 'target': 'Test State',
+215 'command': 'set state', 'new state': True}
+216 test(): {'sender': 'Test StateAlias', 'state': False}
+217 test(): {'sender': 'test()', 'target': 'Test StateAlias',
+218 'command': 'get state'}
+219 test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
+220 test(): {'sender': 'Test State', 'state': True}
+221 test(): {'sender': 'Test StateAlias', 'target': 'Test State',
+222 'command': 'get state'}
+223 test(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True}
+224 test(): {'sender': 'Test StateAlias', 'state': True}
+225 """
+226
+227 CONF_SCHEMA = {
+228 "properties" : { "alias for" : { "type" : "string" }},
+229 "required" : [ "alias for" ],
+230 }
+231 """Schema for StateAlias plugin configuration.
+232
+233 Required configuration key:
+234
+235 - 'alias for': name of aliased state.
+236 """
+237
+238 def process_conf ( self ) -> None :
+239 """Register plugin as bus client."""
+240 self . bus . register (
+241 self . name ,
+242 "StateAlias" ,
+243 [
+244 MessageTemplate (
+245 {
+246 "target" : { "const" : self . conf [ "alias for" ]},
+247 "command" : { "const" : "get state" },
+248 }
+249 ),
+250 MessageTemplate (
+251 {
+252 "target" : { "const" : self . conf [ "alias for" ]},
+253 "command" : { "const" : "set state" },
+254 "new state" : { "type" : "boolean" },
+255 }
+256 ),
+257 MessageTemplate (
+258 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+259 ),
+260 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+261 ],
+262 [
+263 (
+264 [
+265 MessageTemplate (
+266 {
+267 "target" : { "const" : self . name },
+268 "command" : { "const" : "get state" },
+269 }
+270 )
+271 ],
+272 self . _get_state ,
+273 ),
+274 (
+275 [
+276 MessageTemplate (
+277 {
+278 "target" : { "const" : self . name },
+279 "command" : { "const" : "set state" },
+280 "new state" : { "type" : "boolean" },
+281 }
+282 )
+283 ],
+284 self . _set_state ,
+285 ),
+286 (
+287 [
+288 MessageTemplate (
+289 {
+290 "sender" : { "const" : self . conf [ "alias for" ]},
+291 "state" : { "type" : "boolean" },
+292 }
+293 )
+294 ],
+295 self . _translate ,
+296 ),
+297 ],
+298 )
+299
+300 async def _get_state ( self , message : Message ) -> None :
+301 await self . bus . send (
+302 Message (
+303 self . name , { "target" : self . conf [ "alias for" ], "command" : "get state" }
+304 )
+305 )
+306
+307 async def _set_state ( self , message : Message ) -> None :
+308 await self . bus . send (
+309 Message (
+310 self . name ,
+311 {
+312 "target" : self . conf [ "alias for" ],
+313 "command" : "set state" ,
+314 "new state" : message [ "new state" ],
+315 },
+316 )
+317 )
+318
+319 async def _translate ( self , message : Message ) -> None :
+320 alias_message = Message ( self . name )
+321 if "event" in message and message [ "event" ] == "changed" :
+322 alias_message [ "event" ] = "changed"
+323 alias_message [ "state" ] = message [ "state" ]
+324 await self . bus . send ( alias_message )
+325
+326 async def run ( self ) -> None :
+327 """Run no code proactively."""
+328 pass
+
+
+
+ Define an alias for another state.
+
+
The "alias for" configuration key gets the name for the other state that
+is aliased by the StateAlias plugin instance.
+
+
The "get state" and "set state" commands are forwarded to and the
+"changed" events and "state" messages are forwarded from this other
+state:
+
+
+
>>> import asyncio
+>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test State" : { "plugin" : "State" },
+... "Test StateAlias" : { "plugin" : "StateAlias" ,
+... "alias for" : "Test State" }},
+... [{ "target" : "Test State" , "command" : "get state" },
+... { "target" : "Test StateAlias" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test StateAlias" , "command" : "get state" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered', ...
+test(): {'sender': 'test()', 'target': 'Test State',
+ 'command': 'get state'}
+test(): {'sender': 'test()', 'target': 'Test StateAlias',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test State', 'state': False}
+test(): {'sender': 'test()', 'target': 'Test State',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test StateAlias', 'target': 'Test State',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test StateAlias', 'state': False}
+test(): {'sender': 'test()', 'target': 'Test StateAlias',
+ 'command': 'get state'}
+test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
+test(): {'sender': 'Test State', 'state': True}
+test(): {'sender': 'Test StateAlias', 'target': 'Test State',
+ 'command': 'get state'}
+test(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True}
+test(): {'sender': 'Test StateAlias', 'state': True}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+{'properties': {'alias for': {'type': 'string'}}, 'required': ['alias for']}
+
+
+
+
+
+
Schema for StateAlias plugin configuration.
+
+
Required configuration key:
+
+
+'alias for': name of aliased state.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
238 def process_conf ( self ) -> None :
+239 """Register plugin as bus client."""
+240 self . bus . register (
+241 self . name ,
+242 "StateAlias" ,
+243 [
+244 MessageTemplate (
+245 {
+246 "target" : { "const" : self . conf [ "alias for" ]},
+247 "command" : { "const" : "get state" },
+248 }
+249 ),
+250 MessageTemplate (
+251 {
+252 "target" : { "const" : self . conf [ "alias for" ]},
+253 "command" : { "const" : "set state" },
+254 "new state" : { "type" : "boolean" },
+255 }
+256 ),
+257 MessageTemplate (
+258 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+259 ),
+260 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+261 ],
+262 [
+263 (
+264 [
+265 MessageTemplate (
+266 {
+267 "target" : { "const" : self . name },
+268 "command" : { "const" : "get state" },
+269 }
+270 )
+271 ],
+272 self . _get_state ,
+273 ),
+274 (
+275 [
+276 MessageTemplate (
+277 {
+278 "target" : { "const" : self . name },
+279 "command" : { "const" : "set state" },
+280 "new state" : { "type" : "boolean" },
+281 }
+282 )
+283 ],
+284 self . _set_state ,
+285 ),
+286 (
+287 [
+288 MessageTemplate (
+289 {
+290 "sender" : { "const" : self . conf [ "alias for" ]},
+291 "state" : { "type" : "boolean" },
+292 }
+293 )
+294 ],
+295 self . _translate ,
+296 ),
+297 ],
+298 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
326 async def run ( self ) -> None :
+327 """Run no code proactively."""
+328 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 331 class AndState ( BasePlugin ):
+332 """Define conjunction of states.
+333
+334 The "states" configuration key gets an array of states to be combined.
+335 An AndState plugin client reacts to "get state" commands and sends
+336 "changed" events when a change in one of the combined states leads to
+337 a change for the conjunction:
+338 >>> import asyncio
+339 >>> import controlpi
+340 >>> asyncio.run(controlpi.test(
+341 ... {"Test State 1": {"plugin": "State"},
+342 ... "Test State 2": {"plugin": "State"},
+343 ... "Test AndState": {"plugin": "AndState",
+344 ... "states": ["Test State 1", "Test State 2"]}},
+345 ... [{"target": "Test State 1", "command": "set state",
+346 ... "new state": True},
+347 ... {"target": "Test State 2", "command": "set state",
+348 ... "new state": True},
+349 ... {"target": "Test State 1", "command": "set state",
+350 ... "new state": False},
+351 ... {"target": "Test AndState", "command": "get state"},
+352 ... {"target": "Test AndState", "command": "get sources"}]))
+353 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+354 test(): {'sender': '', 'event': 'registered', ...
+355 test(): {'sender': 'test()', 'target': 'Test State 1',
+356 'command': 'set state', 'new state': True}
+357 test(): {'sender': 'test()', 'target': 'Test State 2',
+358 'command': 'set state', 'new state': True}
+359 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+360 test(): {'sender': 'test()', 'target': 'Test State 1',
+361 'command': 'set state', 'new state': False}
+362 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+363 test(): {'sender': 'test()', 'target': 'Test AndState',
+364 'command': 'get state'}
+365 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+366 test(): {'sender': 'Test AndState', 'event': 'changed', 'state': True}
+367 test(): {'sender': 'test()', 'target': 'Test AndState',
+368 'command': 'get sources'}
+369 test(): {'sender': 'Test AndState', 'state': True}
+370 test(): {'sender': 'Test AndState', 'event': 'changed', 'state': False}
+371 test(): {'sender': 'Test AndState',
+372 'states': ['Test State 1', 'Test State 2']}
+373 """
+374
+375 CONF_SCHEMA = {
+376 "properties" : { "states" : { "type" : "array" , "items" : { "type" : "string" }}},
+377 "required" : [ "states" ],
+378 }
+379 """Schema for AndState plugin configuration.
+380
+381 Required configuration key:
+382
+383 - 'states': list of names of combined states.
+384 """
+385
+386 def process_conf ( self ) -> None :
+387 """Register plugin as bus client."""
+388 updates : List [ MessageTemplate ] = []
+389 self . states : Dict [ str , bool ] = {}
+390 for state in self . conf [ "states" ]:
+391 updates . append (
+392 MessageTemplate (
+393 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+394 )
+395 )
+396 self . states [ state ] = False
+397 self . state : bool = all ( self . states . values ())
+398 self . bus . register (
+399 self . name ,
+400 "AndState" ,
+401 [
+402 MessageTemplate (
+403 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+404 ),
+405 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+406 MessageTemplate (
+407 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+408 ),
+409 ],
+410 [
+411 (
+412 [
+413 MessageTemplate (
+414 {
+415 "target" : { "const" : self . name },
+416 "command" : { "const" : "get state" },
+417 }
+418 )
+419 ],
+420 self . _get_state ,
+421 ),
+422 (
+423 [
+424 MessageTemplate (
+425 {
+426 "target" : { "const" : self . name },
+427 "command" : { "const" : "get sources" },
+428 }
+429 )
+430 ],
+431 self . _get_sources ,
+432 ),
+433 ( updates , self . _update ),
+434 ],
+435 )
+436
+437 async def _get_state ( self , message : Message ) -> None :
+438 await self . bus . send ( Message ( self . name , { "state" : self . state }))
+439
+440 async def _get_sources ( self , message : Message ) -> None :
+441 source_states = list ( self . states . keys ())
+442 await self . bus . send ( Message ( self . name , { "states" : source_states }))
+443
+444 async def _update ( self , message : Message ) -> None :
+445 assert isinstance ( message [ "sender" ], str )
+446 assert isinstance ( message [ "state" ], bool )
+447 self . states [ message [ "sender" ]] = message [ "state" ]
+448 new_state = all ( self . states . values ())
+449 if self . state != new_state :
+450 self . state = new_state
+451 await self . bus . send (
+452 Message ( self . name , { "event" : "changed" , "state" : self . state })
+453 )
+454
+455 async def run ( self ) -> None :
+456 """Run no code proactively."""
+457 pass
+
+
+
+ Define conjunction of states.
+
+
The "states" configuration key gets an array of states to be combined.
+An AndState plugin client reacts to "get state" commands and sends
+"changed" events when a change in one of the combined states leads to
+a change for the conjunction:
+
+
+
>>> import asyncio
+>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test State 1" : { "plugin" : "State" },
+... "Test State 2" : { "plugin" : "State" },
+... "Test AndState" : { "plugin" : "AndState" ,
+... "states" : [ "Test State 1" , "Test State 2" ]}},
+... [{ "target" : "Test State 1" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State 2" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State 1" , "command" : "set state" ,
+... "new state" : False },
+... { "target" : "Test AndState" , "command" : "get state" },
+... { "target" : "Test AndState" , "command" : "get sources" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered', ...
+test(): {'sender': 'test()', 'target': 'Test State 1',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'test()', 'target': 'Test State 2',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test State 1',
+ 'command': 'set state', 'new state': False}
+test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test AndState',
+ 'command': 'get state'}
+test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+test(): {'sender': 'Test AndState', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test AndState',
+ 'command': 'get sources'}
+test(): {'sender': 'Test AndState', 'state': True}
+test(): {'sender': 'Test AndState', 'event': 'changed', 'state': False}
+test(): {'sender': 'Test AndState',
+ 'states': ['Test State 1', 'Test State 2']}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+{'properties': {'states': {'type': 'array', 'items': {'type': 'string'}}}, 'required': ['states']}
+
+
+
+
+
+
Schema for AndState plugin configuration.
+
+
Required configuration key:
+
+
+'states': list of names of combined states.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
386 def process_conf ( self ) -> None :
+387 """Register plugin as bus client."""
+388 updates : List [ MessageTemplate ] = []
+389 self . states : Dict [ str , bool ] = {}
+390 for state in self . conf [ "states" ]:
+391 updates . append (
+392 MessageTemplate (
+393 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+394 )
+395 )
+396 self . states [ state ] = False
+397 self . state : bool = all ( self . states . values ())
+398 self . bus . register (
+399 self . name ,
+400 "AndState" ,
+401 [
+402 MessageTemplate (
+403 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+404 ),
+405 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+406 MessageTemplate (
+407 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+408 ),
+409 ],
+410 [
+411 (
+412 [
+413 MessageTemplate (
+414 {
+415 "target" : { "const" : self . name },
+416 "command" : { "const" : "get state" },
+417 }
+418 )
+419 ],
+420 self . _get_state ,
+421 ),
+422 (
+423 [
+424 MessageTemplate (
+425 {
+426 "target" : { "const" : self . name },
+427 "command" : { "const" : "get sources" },
+428 }
+429 )
+430 ],
+431 self . _get_sources ,
+432 ),
+433 ( updates , self . _update ),
+434 ],
+435 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
455 async def run ( self ) -> None :
+456 """Run no code proactively."""
+457 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 460 class OrState ( BasePlugin ):
+461 """Define disjunction of states.
+462
+463 The "states" configuration key gets an array of states to be combined.
+464 An OrState plugin client reacts to "get state" commands and sends
+465 "changed" events when a change in one of the combined states leads to
+466 a change for the disjunction:
+467 >>> import asyncio
+468 >>> import controlpi
+469 >>> asyncio.run(controlpi.test(
+470 ... {"Test State 1": {"plugin": "State"},
+471 ... "Test State 2": {"plugin": "State"},
+472 ... "Test OrState": {"plugin": "OrState",
+473 ... "states": ["Test State 1", "Test State 2"]}},
+474 ... [{"target": "Test State 1", "command": "set state",
+475 ... "new state": True},
+476 ... {"target": "Test State 2", "command": "set state",
+477 ... "new state": True},
+478 ... {"target": "Test State 1", "command": "set state",
+479 ... "new state": False},
+480 ... {"target": "Test OrState", "command": "get state"},
+481 ... {"target": "Test OrState", "command": "get sources"}]))
+482 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+483 test(): {'sender': '', 'event': 'registered', ...
+484 test(): {'sender': 'test()', 'target': 'Test State 1',
+485 'command': 'set state', 'new state': True}
+486 test(): {'sender': 'test()', 'target': 'Test State 2',
+487 'command': 'set state', 'new state': True}
+488 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+489 test(): {'sender': 'test()', 'target': 'Test State 1',
+490 'command': 'set state', 'new state': False}
+491 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+492 test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
+493 test(): {'sender': 'test()', 'target': 'Test OrState',
+494 'command': 'get state'}
+495 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+496 test(): {'sender': 'test()', 'target': 'Test OrState',
+497 'command': 'get sources'}
+498 test(): {'sender': 'Test OrState', 'state': True}
+499 test(): {'sender': 'Test OrState',
+500 'states': ['Test State 1', 'Test State 2']}
+501 """
+502
+503 CONF_SCHEMA = {
+504 "properties" : { "states" : { "type" : "array" , "items" : { "type" : "string" }}},
+505 "required" : [ "states" ],
+506 }
+507 """Schema for OrState plugin configuration.
+508
+509 Required configuration key:
+510
+511 - 'states': list of names of combined states.
+512 """
+513
+514 def process_conf ( self ) -> None :
+515 """Register plugin as bus client."""
+516 updates : List [ MessageTemplate ] = []
+517 self . states : Dict [ str , bool ] = {}
+518 for state in self . conf [ "states" ]:
+519 updates . append (
+520 MessageTemplate (
+521 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+522 )
+523 )
+524 self . states [ state ] = False
+525 self . state : bool = any ( self . states . values ())
+526 self . bus . register (
+527 self . name ,
+528 "OrState" ,
+529 [
+530 MessageTemplate (
+531 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+532 ),
+533 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+534 MessageTemplate (
+535 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+536 ),
+537 ],
+538 [
+539 (
+540 [
+541 MessageTemplate (
+542 {
+543 "target" : { "const" : self . name },
+544 "command" : { "const" : "get state" },
+545 }
+546 )
+547 ],
+548 self . _get_state ,
+549 ),
+550 (
+551 [
+552 MessageTemplate (
+553 {
+554 "target" : { "const" : self . name },
+555 "command" : { "const" : "get sources" },
+556 }
+557 )
+558 ],
+559 self . _get_sources ,
+560 ),
+561 ( updates , self . _update ),
+562 ],
+563 )
+564
+565 async def _get_state ( self , message : Message ) -> None :
+566 await self . bus . send ( Message ( self . name , { "state" : self . state }))
+567
+568 async def _get_sources ( self , message : Message ) -> None :
+569 source_states = list ( self . states . keys ())
+570 await self . bus . send ( Message ( self . name , { "states" : source_states }))
+571
+572 async def _update ( self , message : Message ) -> None :
+573 assert isinstance ( message [ "sender" ], str )
+574 assert isinstance ( message [ "state" ], bool )
+575 self . states [ message [ "sender" ]] = message [ "state" ]
+576 new_state = any ( self . states . values ())
+577 if self . state != new_state :
+578 self . state = new_state
+579 await self . bus . send (
+580 Message ( self . name , { "event" : "changed" , "state" : self . state })
+581 )
+582
+583 async def run ( self ) -> None :
+584 """Run no code proactively."""
+585 pass
+
+
+
+ Define disjunction of states.
+
+
The "states" configuration key gets an array of states to be combined.
+An OrState plugin client reacts to "get state" commands and sends
+"changed" events when a change in one of the combined states leads to
+a change for the disjunction:
+
+
+
>>> import asyncio
+>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test State 1" : { "plugin" : "State" },
+... "Test State 2" : { "plugin" : "State" },
+... "Test OrState" : { "plugin" : "OrState" ,
+... "states" : [ "Test State 1" , "Test State 2" ]}},
+... [{ "target" : "Test State 1" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State 2" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State 1" , "command" : "set state" ,
+... "new state" : False },
+... { "target" : "Test OrState" , "command" : "get state" },
+... { "target" : "Test OrState" , "command" : "get sources" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered', ...
+test(): {'sender': 'test()', 'target': 'Test State 1',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'test()', 'target': 'Test State 2',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test State 1',
+ 'command': 'set state', 'new state': False}
+test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test OrState',
+ 'command': 'get state'}
+test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+test(): {'sender': 'test()', 'target': 'Test OrState',
+ 'command': 'get sources'}
+test(): {'sender': 'Test OrState', 'state': True}
+test(): {'sender': 'Test OrState',
+ 'states': ['Test State 1', 'Test State 2']}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+{'properties': {'states': {'type': 'array', 'items': {'type': 'string'}}}, 'required': ['states']}
+
+
+
+
+
+
Schema for OrState plugin configuration.
+
+
Required configuration key:
+
+
+'states': list of names of combined states.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
514 def process_conf ( self ) -> None :
+515 """Register plugin as bus client."""
+516 updates : List [ MessageTemplate ] = []
+517 self . states : Dict [ str , bool ] = {}
+518 for state in self . conf [ "states" ]:
+519 updates . append (
+520 MessageTemplate (
+521 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+522 )
+523 )
+524 self . states [ state ] = False
+525 self . state : bool = any ( self . states . values ())
+526 self . bus . register (
+527 self . name ,
+528 "OrState" ,
+529 [
+530 MessageTemplate (
+531 { "event" : { "const" : "changed" }, "state" : { "type" : "boolean" }}
+532 ),
+533 MessageTemplate ({ "state" : { "type" : "boolean" }}),
+534 MessageTemplate (
+535 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+536 ),
+537 ],
+538 [
+539 (
+540 [
+541 MessageTemplate (
+542 {
+543 "target" : { "const" : self . name },
+544 "command" : { "const" : "get state" },
+545 }
+546 )
+547 ],
+548 self . _get_state ,
+549 ),
+550 (
+551 [
+552 MessageTemplate (
+553 {
+554 "target" : { "const" : self . name },
+555 "command" : { "const" : "get sources" },
+556 }
+557 )
+558 ],
+559 self . _get_sources ,
+560 ),
+561 ( updates , self . _update ),
+562 ],
+563 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
583 async def run ( self ) -> None :
+584 """Run no code proactively."""
+585 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 588 class AndSet ( BasePlugin ):
+589 """Set state based on conjunction of other states.
+590
+591 The "input states" configuration key gets an array of states used to
+592 determine the state in the "output state" configuration key:
+593 >>> import asyncio
+594 >>> import controlpi
+595 >>> asyncio.run(controlpi.test(
+596 ... {"Test State 1": {"plugin": "State"},
+597 ... "Test State 2": {"plugin": "State"},
+598 ... "Test State 3": {"plugin": "State"},
+599 ... "Test AndSet": {"plugin": "AndSet",
+600 ... "input states": ["Test State 1",
+601 ... "Test State 2"],
+602 ... "output state": "Test State 3"}},
+603 ... [{"target": "Test State 1", "command": "set state",
+604 ... "new state": True},
+605 ... {"target": "Test State 2", "command": "set state",
+606 ... "new state": True},
+607 ... {"target": "Test AndSet", "command": "get state"},
+608 ... {"target": "Test State 1", "command": "set state",
+609 ... "new state": False},
+610 ... {"target": "Test AndSet", "command": "get state"},
+611 ... {"target": "Test AndSet", "command": "get sources"}]))
+612 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+613 test(): {'sender': '', 'event': 'registered', ...
+614 test(): {'sender': 'test()', 'target': 'Test State 1',
+615 'command': 'set state', 'new state': True}
+616 test(): {'sender': 'test()', 'target': 'Test State 2',
+617 'command': 'set state', 'new state': True}
+618 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+619 test(): {'sender': 'test()', 'target': 'Test AndSet',
+620 'command': 'get state'}
+621 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+622 test(): {'sender': 'test()', 'target': 'Test State 1',
+623 'command': 'set state', 'new state': False}
+624 test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+625 'command': 'set state', 'new state': False}
+626 test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+627 'command': 'set state', 'new state': True}
+628 test(): {'sender': 'test()', 'target': 'Test AndSet',
+629 'command': 'get state'}
+630 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+631 test(): {'sender': 'Test State 3', 'state': False}
+632 test(): {'sender': 'Test State 3', 'event': 'changed', 'state': True}
+633 test(): {'sender': 'test()', 'target': 'Test AndSet',
+634 'command': 'get sources'}
+635 test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+636 'command': 'set state', 'new state': True}
+637 test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+638 'command': 'set state', 'new state': False}
+639 test(): {'sender': 'Test AndSet',
+640 'states': ['Test State 1', 'Test State 2']}
+641 test(): {'sender': 'Test State 3', 'state': True}
+642 test(): {'sender': 'Test State 3', 'event': 'changed', 'state': False}
+643 """
+644
+645 CONF_SCHEMA = {
+646 "properties" : {
+647 "input states" : { "type" : "array" , "items" : { "type" : "string" }},
+648 "output state" : { "type" : "string" },
+649 },
+650 "required" : [ "input states" , "output state" ],
+651 }
+652 """Schema for AndSet plugin configuration.
+653
+654 Required configuration keys:
+655
+656 - 'input states': list of names of combined states.
+657 - 'output state': name of state to be set.
+658 """
+659
+660 def process_conf ( self ) -> None :
+661 """Register plugin as bus client."""
+662 updates : List [ MessageTemplate ] = []
+663 self . states : Dict [ str , bool ] = {}
+664 for state in self . conf [ "input states" ]:
+665 updates . append (
+666 MessageTemplate (
+667 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+668 )
+669 )
+670 self . states [ state ] = False
+671 self . state : bool = all ( self . states . values ())
+672 self . bus . register (
+673 self . name ,
+674 "AndSet" ,
+675 [
+676 MessageTemplate (
+677 {
+678 "target" : { "const" : self . conf [ "output state" ]},
+679 "command" : { "const" : "set state" },
+680 "new state" : { "type" : "boolean" },
+681 }
+682 ),
+683 MessageTemplate (
+684 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+685 ),
+686 ],
+687 [
+688 (
+689 [
+690 MessageTemplate (
+691 {
+692 "target" : { "const" : self . name },
+693 "command" : { "const" : "get state" },
+694 }
+695 )
+696 ],
+697 self . _get_state ,
+698 ),
+699 (
+700 [
+701 MessageTemplate (
+702 {
+703 "target" : { "const" : self . name },
+704 "command" : { "const" : "get sources" },
+705 }
+706 )
+707 ],
+708 self . _get_sources ,
+709 ),
+710 ( updates , self . _update ),
+711 ],
+712 )
+713
+714 async def _get_state ( self , message : Message ) -> None :
+715 await self . bus . send (
+716 Message (
+717 self . name ,
+718 {
+719 "target" : self . conf [ "output state" ],
+720 "command" : "set state" ,
+721 "new state" : self . state ,
+722 },
+723 )
+724 )
+725
+726 async def _get_sources ( self , message : Message ) -> None :
+727 source_states = list ( self . states . keys ())
+728 await self . bus . send ( Message ( self . name , { "states" : source_states }))
+729
+730 async def _update ( self , message : Message ) -> None :
+731 assert isinstance ( message [ "sender" ], str )
+732 assert isinstance ( message [ "state" ], bool )
+733 self . states [ message [ "sender" ]] = message [ "state" ]
+734 new_state = all ( self . states . values ())
+735 if self . state != new_state :
+736 self . state = new_state
+737 await self . bus . send (
+738 Message (
+739 self . name ,
+740 {
+741 "target" : self . conf [ "output state" ],
+742 "command" : "set state" ,
+743 "new state" : self . state ,
+744 },
+745 )
+746 )
+747
+748 async def run ( self ) -> None :
+749 """Run no code proactively."""
+750 pass
+
+
+
+ Set state based on conjunction of other states.
+
+
The "input states" configuration key gets an array of states used to
+determine the state in the "output state" configuration key:
+
+
+
>>> import asyncio
+>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test State 1" : { "plugin" : "State" },
+... "Test State 2" : { "plugin" : "State" },
+... "Test State 3" : { "plugin" : "State" },
+... "Test AndSet" : { "plugin" : "AndSet" ,
+... "input states" : [ "Test State 1" ,
+... "Test State 2" ],
+... "output state" : "Test State 3" }},
+... [{ "target" : "Test State 1" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State 2" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test AndSet" , "command" : "get state" },
+... { "target" : "Test State 1" , "command" : "set state" ,
+... "new state" : False },
+... { "target" : "Test AndSet" , "command" : "get state" },
+... { "target" : "Test AndSet" , "command" : "get sources" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered', ...
+test(): {'sender': 'test()', 'target': 'Test State 1',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'test()', 'target': 'Test State 2',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test AndSet',
+ 'command': 'get state'}
+test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test State 1',
+ 'command': 'set state', 'new state': False}
+test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+ 'command': 'set state', 'new state': False}
+test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'test()', 'target': 'Test AndSet',
+ 'command': 'get state'}
+test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+test(): {'sender': 'Test State 3', 'state': False}
+test(): {'sender': 'Test State 3', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test AndSet',
+ 'command': 'get sources'}
+test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test AndSet', 'target': 'Test State 3',
+ 'command': 'set state', 'new state': False}
+test(): {'sender': 'Test AndSet',
+ 'states': ['Test State 1', 'Test State 2']}
+test(): {'sender': 'Test State 3', 'state': True}
+test(): {'sender': 'Test State 3', 'event': 'changed', 'state': False}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+
+ {'properties': {'input states': {'type': 'array', 'items': {'type': 'string'}}, 'output state': {'type': 'string'}}, 'required': ['input states', 'output state']}
+
+
+
+
+
+
Schema for AndSet plugin configuration.
+
+
Required configuration keys:
+
+
+'input states': list of names of combined states.
+'output state': name of state to be set.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
660 def process_conf ( self ) -> None :
+661 """Register plugin as bus client."""
+662 updates : List [ MessageTemplate ] = []
+663 self . states : Dict [ str , bool ] = {}
+664 for state in self . conf [ "input states" ]:
+665 updates . append (
+666 MessageTemplate (
+667 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+668 )
+669 )
+670 self . states [ state ] = False
+671 self . state : bool = all ( self . states . values ())
+672 self . bus . register (
+673 self . name ,
+674 "AndSet" ,
+675 [
+676 MessageTemplate (
+677 {
+678 "target" : { "const" : self . conf [ "output state" ]},
+679 "command" : { "const" : "set state" },
+680 "new state" : { "type" : "boolean" },
+681 }
+682 ),
+683 MessageTemplate (
+684 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+685 ),
+686 ],
+687 [
+688 (
+689 [
+690 MessageTemplate (
+691 {
+692 "target" : { "const" : self . name },
+693 "command" : { "const" : "get state" },
+694 }
+695 )
+696 ],
+697 self . _get_state ,
+698 ),
+699 (
+700 [
+701 MessageTemplate (
+702 {
+703 "target" : { "const" : self . name },
+704 "command" : { "const" : "get sources" },
+705 }
+706 )
+707 ],
+708 self . _get_sources ,
+709 ),
+710 ( updates , self . _update ),
+711 ],
+712 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
748 async def run ( self ) -> None :
+749 """Run no code proactively."""
+750 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 753 class OrSet ( BasePlugin ):
+754 """Set state based on disjunction of other states.
+755
+756 The "input states" configuration key gets an array of states used to
+757 determine the state in the "output state" configuration key:
+758 >>> import asyncio
+759 >>> import controlpi
+760 >>> asyncio.run(controlpi.test(
+761 ... {"Test State 1": {"plugin": "State"},
+762 ... "Test State 2": {"plugin": "State"},
+763 ... "Test State 3": {"plugin": "State"},
+764 ... "Test OrSet": {"plugin": "OrSet",
+765 ... "input states": ["Test State 1",
+766 ... "Test State 2"],
+767 ... "output state": "Test State 3"}},
+768 ... [{"target": "Test State 1", "command": "set state",
+769 ... "new state": True},
+770 ... {"target": "Test OrSet", "command": "get state"},
+771 ... {"target": "Test State 2", "command": "set state",
+772 ... "new state": True},
+773 ... {"target": "Test State 1", "command": "set state",
+774 ... "new state": False},
+775 ... {"target": "Test OrSet", "command": "get sources"}]))
+776 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+777 test(): {'sender': '', 'event': 'registered', ...
+778 test(): {'sender': 'test()', 'target': 'Test State 1',
+779 'command': 'set state', 'new state': True}
+780 test(): {'sender': 'test()', 'target': 'Test OrSet',
+781 'command': 'get state'}
+782 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+783 test(): {'sender': 'test()', 'target': 'Test State 2',
+784 'command': 'set state', 'new state': True}
+785 test(): {'sender': 'Test OrSet', 'target': 'Test State 3',
+786 'command': 'set state', 'new state': False}
+787 test(): {'sender': 'Test OrSet', 'target': 'Test State 3',
+788 'command': 'set state', 'new state': True}
+789 test(): {'sender': 'test()', 'target': 'Test State 1',
+790 'command': 'set state', 'new state': False}
+791 test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+792 test(): {'sender': 'Test State 3', 'state': False}
+793 test(): {'sender': 'Test State 3', 'event': 'changed', 'state': True}
+794 test(): {'sender': 'test()', 'target': 'Test OrSet',
+795 'command': 'get sources'}
+796 test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+797 test(): {'sender': 'Test OrSet',
+798 'states': ['Test State 1', 'Test State 2']}
+799 """
+800
+801 CONF_SCHEMA = {
+802 "properties" : {
+803 "input states" : { "type" : "array" , "items" : { "type" : "string" }},
+804 "output state" : { "type" : "string" },
+805 },
+806 "required" : [ "input states" , "output state" ],
+807 }
+808 """Schema for OrSet plugin configuration.
+809
+810 Required configuration keys:
+811
+812 - 'input states': list of names of combined states.
+813 - 'output state': name of state to be set.
+814 """
+815
+816 def process_conf ( self ) -> None :
+817 """Register plugin as bus client."""
+818 updates : List [ MessageTemplate ] = []
+819 self . states : Dict [ str , bool ] = {}
+820 for state in self . conf [ "input states" ]:
+821 updates . append (
+822 MessageTemplate (
+823 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+824 )
+825 )
+826 self . states [ state ] = False
+827 self . state : bool = any ( self . states . values ())
+828 self . bus . register (
+829 self . name ,
+830 "AndSet" ,
+831 [
+832 MessageTemplate (
+833 {
+834 "target" : { "const" : self . conf [ "output state" ]},
+835 "command" : { "const" : "set state" },
+836 "new state" : { "type" : "boolean" },
+837 }
+838 ),
+839 MessageTemplate (
+840 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+841 ),
+842 ],
+843 [
+844 (
+845 [
+846 MessageTemplate (
+847 {
+848 "target" : { "const" : self . name },
+849 "command" : { "const" : "get state" },
+850 }
+851 )
+852 ],
+853 self . _get_state ,
+854 ),
+855 (
+856 [
+857 MessageTemplate (
+858 {
+859 "target" : { "const" : self . name },
+860 "command" : { "const" : "get sources" },
+861 }
+862 )
+863 ],
+864 self . _get_sources ,
+865 ),
+866 ( updates , self . _update ),
+867 ],
+868 )
+869
+870 async def _get_state ( self , message : Message ) -> None :
+871 await self . bus . send (
+872 Message (
+873 self . name ,
+874 {
+875 "target" : self . conf [ "output state" ],
+876 "command" : "set state" ,
+877 "new state" : self . state ,
+878 },
+879 )
+880 )
+881
+882 async def _get_sources ( self , message : Message ) -> None :
+883 source_states = list ( self . states . keys ())
+884 await self . bus . send ( Message ( self . name , { "states" : source_states }))
+885
+886 async def _update ( self , message : Message ) -> None :
+887 assert isinstance ( message [ "sender" ], str )
+888 assert isinstance ( message [ "state" ], bool )
+889 self . states [ message [ "sender" ]] = message [ "state" ]
+890 new_state = any ( self . states . values ())
+891 if self . state != new_state :
+892 self . state = new_state
+893 await self . bus . send (
+894 Message (
+895 self . name ,
+896 {
+897 "target" : self . conf [ "output state" ],
+898 "command" : "set state" ,
+899 "new state" : self . state ,
+900 },
+901 )
+902 )
+903
+904 async def run ( self ) -> None :
+905 """Run no code proactively."""
+906 pass
+
+
+
+ Set state based on disjunction of other states.
+
+
The "input states" configuration key gets an array of states used to
+determine the state in the "output state" configuration key:
+
+
+
>>> import asyncio
+>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test State 1" : { "plugin" : "State" },
+... "Test State 2" : { "plugin" : "State" },
+... "Test State 3" : { "plugin" : "State" },
+... "Test OrSet" : { "plugin" : "OrSet" ,
+... "input states" : [ "Test State 1" ,
+... "Test State 2" ],
+... "output state" : "Test State 3" }},
+... [{ "target" : "Test State 1" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test OrSet" , "command" : "get state" },
+... { "target" : "Test State 2" , "command" : "set state" ,
+... "new state" : True },
+... { "target" : "Test State 1" , "command" : "set state" ,
+... "new state" : False },
+... { "target" : "Test OrSet" , "command" : "get sources" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered', ...
+test(): {'sender': 'test()', 'target': 'Test State 1',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'test()', 'target': 'Test OrSet',
+ 'command': 'get state'}
+test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test State 2',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'Test OrSet', 'target': 'Test State 3',
+ 'command': 'set state', 'new state': False}
+test(): {'sender': 'Test OrSet', 'target': 'Test State 3',
+ 'command': 'set state', 'new state': True}
+test(): {'sender': 'test()', 'target': 'Test State 1',
+ 'command': 'set state', 'new state': False}
+test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
+test(): {'sender': 'Test State 3', 'state': False}
+test(): {'sender': 'Test State 3', 'event': 'changed', 'state': True}
+test(): {'sender': 'test()', 'target': 'Test OrSet',
+ 'command': 'get sources'}
+test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
+test(): {'sender': 'Test OrSet',
+ 'states': ['Test State 1', 'Test State 2']}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+
+ {'properties': {'input states': {'type': 'array', 'items': {'type': 'string'}}, 'output state': {'type': 'string'}}, 'required': ['input states', 'output state']}
+
+
+
+
+
+
Schema for OrSet plugin configuration.
+
+
Required configuration keys:
+
+
+'input states': list of names of combined states.
+'output state': name of state to be set.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
816 def process_conf ( self ) -> None :
+817 """Register plugin as bus client."""
+818 updates : List [ MessageTemplate ] = []
+819 self . states : Dict [ str , bool ] = {}
+820 for state in self . conf [ "input states" ]:
+821 updates . append (
+822 MessageTemplate (
+823 { "sender" : { "const" : state }, "state" : { "type" : "boolean" }}
+824 )
+825 )
+826 self . states [ state ] = False
+827 self . state : bool = any ( self . states . values ())
+828 self . bus . register (
+829 self . name ,
+830 "AndSet" ,
+831 [
+832 MessageTemplate (
+833 {
+834 "target" : { "const" : self . conf [ "output state" ]},
+835 "command" : { "const" : "set state" },
+836 "new state" : { "type" : "boolean" },
+837 }
+838 ),
+839 MessageTemplate (
+840 { "states" : { "type" : "array" , "items" : { "type" : "string" }}}
+841 ),
+842 ],
+843 [
+844 (
+845 [
+846 MessageTemplate (
+847 {
+848 "target" : { "const" : self . name },
+849 "command" : { "const" : "get state" },
+850 }
+851 )
+852 ],
+853 self . _get_state ,
+854 ),
+855 (
+856 [
+857 MessageTemplate (
+858 {
+859 "target" : { "const" : self . name },
+860 "command" : { "const" : "get sources" },
+861 }
+862 )
+863 ],
+864 self . _get_sources ,
+865 ),
+866 ( updates , self . _update ),
+867 ],
+868 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
904 async def run ( self ) -> None :
+905 """Run no code proactively."""
+906 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/controlpi_plugins/util.html b/doc/api/controlpi_plugins/util.html
new file mode 100644
index 0000000..a2800c1
--- /dev/null
+++ b/doc/api/controlpi_plugins/util.html
@@ -0,0 +1,2680 @@
+
+
+
+
+
+
+ controlpi_plugins.util API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Provide utility plugins for all kinds of systems.
+
+
+Log logs messages on stdout.
+Init sends list of messages on startup and on demand.
+Execute sends configurable list of messages on demand.
+Alias translates messages to an alias.
+
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test Log" : { "plugin" : "Log" ,
+... "filter" : [{ "sender" : { "const" : "Test Alias" }}]},
+... "Test Init" : { "plugin" : "Init" ,
+... "messages" : [{ "id" : 42 , "content" : "Test Message" }]},
+... "Test Alias" : { "plugin" : "Alias" ,
+... "from" : { "sender" : { "const" : "Test Init" },
+... "id" : { "const" : 42 }},
+... "to" : { "id" : "translated" }}}, []))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Log', 'plugin': 'Log',
+ 'sends': [], 'receives': [{'sender': {'const': 'Test Alias'}}]}
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Init', 'plugin': 'Init',
+ 'sends': [{'id': {'const': 42},
+ 'content': {'const': 'Test Message'}}],
+ 'receives': [{'target': {'const': 'Test Init'},
+ 'command': {'const': 'execute'}}]}
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Alias', 'plugin': 'Alias',
+ 'sends': [{'id': {'const': 'translated'}}],
+ 'receives': [{'sender': {'const': 'Test Init'},
+ 'id': {'const': 42}}]}
+test(): {'sender': 'Test Init', 'id': 42,
+ 'content': 'Test Message'}
+test(): {'sender': 'Test Alias', 'id': 'translated',
+ 'content': 'Test Message'}
+Test Log: {'sender': 'Test Alias', 'id': 'translated',
+ 'content': 'Test Message'}
+
+
+
+
+
+
+ View Source
+
+ 1 """Provide utility plugins for all kinds of systems.
+ 2
+ 3 - Log logs messages on stdout.
+ 4 - Init sends list of messages on startup and on demand.
+ 5 - Execute sends configurable list of messages on demand.
+ 6 - Alias translates messages to an alias.
+ 7
+ 8 >>> import controlpi
+ 9 >>> asyncio.run(controlpi.test(
+ 10 ... {"Test Log": {"plugin": "Log",
+ 11 ... "filter": [{"sender": {"const": "Test Alias"}}]},
+ 12 ... "Test Init": {"plugin": "Init",
+ 13 ... "messages": [{"id": 42, "content": "Test Message"}]},
+ 14 ... "Test Alias": {"plugin": "Alias",
+ 15 ... "from": {"sender": {"const": "Test Init"},
+ 16 ... "id": {"const": 42}},
+ 17 ... "to": {"id": "translated"}}}, []))
+ 18 ... # doctest: +NORMALIZE_WHITESPACE
+ 19 test(): {'sender': '', 'event': 'registered',
+ 20 'client': 'Test Log', 'plugin': 'Log',
+ 21 'sends': [], 'receives': [{'sender': {'const': 'Test Alias'}}]}
+ 22 test(): {'sender': '', 'event': 'registered',
+ 23 'client': 'Test Init', 'plugin': 'Init',
+ 24 'sends': [{'id': {'const': 42},
+ 25 'content': {'const': 'Test Message'}}],
+ 26 'receives': [{'target': {'const': 'Test Init'},
+ 27 'command': {'const': 'execute'}}]}
+ 28 test(): {'sender': '', 'event': 'registered',
+ 29 'client': 'Test Alias', 'plugin': 'Alias',
+ 30 'sends': [{'id': {'const': 'translated'}}],
+ 31 'receives': [{'sender': {'const': 'Test Init'},
+ 32 'id': {'const': 42}}]}
+ 33 test(): {'sender': 'Test Init', 'id': 42,
+ 34 'content': 'Test Message'}
+ 35 test(): {'sender': 'Test Alias', 'id': 'translated',
+ 36 'content': 'Test Message'}
+ 37 Test Log: {'sender': 'Test Alias', 'id': 'translated',
+ 38 'content': 'Test Message'}
+ 39 """
+ 40
+ 41 import asyncio
+ 42 from datetime import datetime
+ 43
+ 44 from controlpi import BasePlugin , Message , MessageTemplate
+ 45
+ 46 from typing import List
+ 47
+ 48
+ 49 class Log ( BasePlugin ):
+ 50 """Log messages on stdout.
+ 51
+ 52 The "filter" configuration key gets a list of message templates defining
+ 53 the messages that should be logged by the plugin instance.
+ 54
+ 55 In the following example the first and third message match the given
+ 56 template and are logged by the instance "Test Log", while the second
+ 57 message does not match and is only logged by the test, but not by the
+ 58 Log instance:
+ 59 >>> import controlpi
+ 60 >>> asyncio.run(controlpi.test(
+ 61 ... {"Test Log": {"plugin": "Log",
+ 62 ... "filter": [{"id": {"const": 42}}]}},
+ 63 ... [{"id": 42, "message": "Test Message"},
+ 64 ... {"id": 42.42, "message": "Second Message"},
+ 65 ... {"id": 42, "message": "Third Message"}]))
+ 66 ... # doctest: +NORMALIZE_WHITESPACE
+ 67 test(): {'sender': '', 'event': 'registered',
+ 68 'client': 'Test Log', 'plugin': 'Log',
+ 69 'sends': [], 'receives': [{'id': {'const': 42}}]}
+ 70 test(): {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
+ 71 Test Log: {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
+ 72 test(): {'sender': 'test()', 'id': 42.42, 'message': 'Second Message'}
+ 73 test(): {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
+ 74 Test Log: {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
+ 75
+ 76 The "filter" key is required:
+ 77 >>> asyncio.run(controlpi.test(
+ 78 ... {"Test Log": {"plugin": "Log"}}, []))
+ 79 data must contain ['filter'] properties
+ 80 Configuration for 'Test Log' is not valid.
+ 81
+ 82 The "filter" key has to contain a list of message templates, i.e.,
+ 83 JSON objects:
+ 84 >>> asyncio.run(controlpi.test(
+ 85 ... {"Test Log": {"plugin": "Log",
+ 86 ... "filter": [42]}}, []))
+ 87 data.filter[0] must be object
+ 88 Configuration for 'Test Log' is not valid.
+ 89 """
+ 90
+ 91 CONF_SCHEMA = {
+ 92 "properties" : { "filter" : { "type" : "array" , "items" : { "type" : "object" }}},
+ 93 "required" : [ "filter" ],
+ 94 }
+ 95 """Schema for Log plugin configuration.
+ 96
+ 97 Required configuration key:
+ 98
+ 99 - 'filter': list of message templates to be logged.
+100 """
+101
+102 def process_conf ( self ) -> None :
+103 """Register plugin as bus client."""
+104 self . bus . register ( self . name , "Log" , [], [( self . conf [ "filter" ], self . _log )])
+105
+106 async def _log ( self , message : Message ) -> None :
+107 print ( f " { self . name } : { message } " )
+108
+109 async def run ( self ) -> None :
+110 """Run no code proactively."""
+111 pass
+112
+113
+114 class Init ( BasePlugin ):
+115 """Send list of messages on startup and on demand.
+116
+117 The "messages" configuration key gets a list of messages to be sent on
+118 startup. The same list is sent in reaction to a message with
+119 "target": NAME and "command": "execute".
+120
+121 In the example, the two configured messages are sent twice, once at
+122 startup and a second time in reaction to the "execute" command sent by
+123 the test:
+124 >>> import controlpi
+125 >>> asyncio.run(controlpi.test(
+126 ... {"Test Init": {"plugin": "Init",
+127 ... "messages": [{"id": 42,
+128 ... "content": "Test Message"},
+129 ... {"id": 42.42,
+130 ... "content": "Second Message"}]}},
+131 ... [{"target": "Test Init", "command": "execute"}]))
+132 ... # doctest: +NORMALIZE_WHITESPACE
+133 test(): {'sender': '', 'event': 'registered',
+134 'client': 'Test Init', 'plugin': 'Init',
+135 'sends': [{'id': {'const': 42},
+136 'content': {'const': 'Test Message'}},
+137 {'id': {'const': 42.42},
+138 'content': {'const': 'Second Message'}}],
+139 'receives': [{'target': {'const': 'Test Init'},
+140 'command': {'const': 'execute'}}]}
+141 test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
+142 test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
+143 test(): {'sender': 'test()', 'target': 'Test Init', 'command': 'execute'}
+144 test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
+145 test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
+146
+147 The "messages" key is required:
+148 >>> asyncio.run(controlpi.test(
+149 ... {"Test Init": {"plugin": "Init"}}, []))
+150 data must contain ['messages'] properties
+151 Configuration for 'Test Init' is not valid.
+152
+153 The "messages" key has to contain a list of (partial) messages, i.e.,
+154 JSON objects:
+155 >>> asyncio.run(controlpi.test(
+156 ... {"Test Init": {"plugin": "Init",
+157 ... "messages": [42]}}, []))
+158 data.messages[0] must be object
+159 Configuration for 'Test Init' is not valid.
+160 """
+161
+162 CONF_SCHEMA = {
+163 "properties" : { "messages" : { "type" : "array" , "items" : { "type" : "object" }}},
+164 "required" : [ "messages" ],
+165 }
+166 """Schema for Init plugin configuration.
+167
+168 Required configuration key:
+169
+170 - 'messages': list of messages to be sent.
+171 """
+172
+173 def process_conf ( self ) -> None :
+174 """Register plugin as bus client."""
+175 self . bus . register (
+176 self . name ,
+177 "Init" ,
+178 [
+179 MessageTemplate . from_message ( message )
+180 for message in self . conf [ "messages" ]
+181 ],
+182 [
+183 (
+184 [
+185 MessageTemplate (
+186 {
+187 "target" : { "const" : self . name },
+188 "command" : { "const" : "execute" },
+189 }
+190 )
+191 ],
+192 self . _execute ,
+193 )
+194 ],
+195 )
+196
+197 async def _execute ( self , message : Message ) -> None :
+198 for message in self . conf [ "messages" ]:
+199 await self . bus . send ( Message ( self . name , message ))
+200 # Give immediate reactions to messages opportunity to happen:
+201 await asyncio . sleep ( 0 )
+202
+203 async def run ( self ) -> None :
+204 """Send configured messages on startup."""
+205 for message in self . conf [ "messages" ]:
+206 await self . bus . send ( Message ( self . name , message ))
+207
+208
+209 class Execute ( BasePlugin ):
+210 """Send configurable list of messages on demand.
+211
+212 An Execute plugin instance receives two kinds of commands.
+213 The "set messages" command has a "messages" key with a list of (partial)
+214 messages, which are sent by the Execute instance in reaction to an
+215 "execute" command.
+216
+217 In the example, the first command sent by the test sets two messages,
+218 which are then sent in reaction to the second command sent by the test:
+219 >>> import controlpi
+220 >>> asyncio.run(controlpi.test(
+221 ... {"Test Execute": {"plugin": "Execute"}},
+222 ... [{"target": "Test Execute", "command": "set messages",
+223 ... "messages": [{"id": 42, "content": "Test Message"},
+224 ... {"id": 42.42, "content": "Second Message"}]},
+225 ... {"target": "Test Execute", "command": "execute"}]))
+226 ... # doctest: +NORMALIZE_WHITESPACE
+227 test(): {'sender': '', 'event': 'registered',
+228 'client': 'Test Execute', 'plugin': 'Execute',
+229 'sends': [{}],
+230 'receives': [{'target': {'const': 'Test Execute'},
+231 'command': {'const': 'set messages'},
+232 'messages': {'type': 'array',
+233 'items': {'type': 'object'}}},
+234 {'target': {'const': 'Test Execute'},
+235 'command': {'const': 'execute'}}]}
+236 test(): {'sender': 'test()', 'target': 'Test Execute',
+237 'command': 'set messages',
+238 'messages': [{'id': 42, 'content': 'Test Message'},
+239 {'id': 42.42, 'content': 'Second Message'}]}
+240 test(): {'sender': 'test()', 'target': 'Test Execute',
+241 'command': 'execute'}
+242 test(): {'sender': 'Test Execute', 'id': 42,
+243 'content': 'Test Message'}
+244 test(): {'sender': 'Test Execute', 'id': 42.42,
+245 'content': 'Second Message'}
+246 """
+247
+248 CONF_SCHEMA = True
+249 """Schema for Execute plugin configuration.
+250
+251 There are no required or optional configuration keys.
+252 """
+253
+254 def process_conf ( self ) -> None :
+255 """Register plugin as bus client."""
+256 self . messages : List [ Message ] = []
+257 self . bus . register (
+258 self . name ,
+259 "Execute" ,
+260 [ MessageTemplate ()],
+261 [
+262 (
+263 [
+264 MessageTemplate (
+265 {
+266 "target" : { "const" : self . name },
+267 "command" : { "const" : "set messages" },
+268 "messages" : {
+269 "type" : "array" ,
+270 "items" : { "type" : "object" },
+271 },
+272 }
+273 )
+274 ],
+275 self . _set_messages ,
+276 ),
+277 (
+278 [
+279 MessageTemplate (
+280 {
+281 "target" : { "const" : self . name },
+282 "command" : { "const" : "execute" },
+283 }
+284 )
+285 ],
+286 self . _execute ,
+287 ),
+288 ],
+289 )
+290
+291 async def _set_messages ( self , message : Message ) -> None :
+292 assert isinstance ( message [ "messages" ], list )
+293 self . messages = list ( message [ "messages" ])
+294
+295 async def _execute ( self , message : Message ) -> None :
+296 for message in self . messages :
+297 await self . bus . send ( Message ( self . name , message ))
+298 # Give immediate reactions to messages opportunity to happen:
+299 await asyncio . sleep ( 0 )
+300
+301 async def run ( self ) -> None :
+302 """Run no code proactively."""
+303 pass
+304
+305
+306 class Alias ( BasePlugin ):
+307 """Translate messages to an alias.
+308
+309 The "from" configuration key gets a message template and the
+310 configuration key "to" a (partial) message. The "translate"
+311 configuration key contains pairs of message keys, where the "from"
+312 message key is translated to the "to" message key if present in the
+313 message.
+314
+315 All messages matching the "from" template are received by the Alias
+316 instance and a message translated by adding the keys and values of the
+317 "to" message and the translated key-value pairs according to
+318 "translate" is sent. Keys that are not "sender" and not modified by
+319 "to" or "translate" are retained.
+320
+321 In the example, the two messages sent by the test are translated by the
+322 Alias instance and the translated messages are sent by it preserving
+323 the "content" keys:
+324 >>> import controlpi
+325 >>> asyncio.run(controlpi.test(
+326 ... {"Test Alias": {"plugin": "Alias",
+327 ... "from": {"id": {"const": 42}},
+328 ... "to": {"id": "translated"},
+329 ... "translate": [{'from': "old", "to": "new"}]}},
+330 ... [{"id": 42, "content": "Test Message", "old": "content"},
+331 ... {"id": 42, "content": "Second Message", "old": "content"}]))
+332 ... # doctest: +NORMALIZE_WHITESPACE
+333 test(): {'sender': '', 'event': 'registered',
+334 'client': 'Test Alias', 'plugin': 'Alias',
+335 'sends': [{'id': {'const': 'translated'}}],
+336 'receives': [{'id': {'const': 42}}]}
+337 test(): {'sender': 'test()', 'id': 42,
+338 'content': 'Test Message', 'old': 'content'}
+339 test(): {'sender': 'test()', 'id': 42,
+340 'content': 'Second Message', 'old': 'content'}
+341 test(): {'sender': 'Test Alias', 'id': 'translated',
+342 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+343 test(): {'sender': 'Test Alias', 'id': 'translated',
+344 'content': 'Second Message', 'old': 'content', 'new': 'content'}
+345
+346 An Alias instance can also translate to a list of messages instead of
+347 a single message:
+348 >>> asyncio.run(controlpi.test(
+349 ... {"Test Alias": {"plugin": "Alias",
+350 ... "from": {"id": {"const": 42}},
+351 ... "to": [{"id": "first"}, {"id": "second"}],
+352 ... "translate": [{'from': "old", "to": "new"}]}},
+353 ... [{"id": 42, "content": "Test Message", "old": "content"}]))
+354 ... # doctest: +NORMALIZE_WHITESPACE
+355 test(): {'sender': '', 'event': 'registered',
+356 'client': 'Test Alias', 'plugin': 'Alias',
+357 'sends': [{'id': {'const': 'first'}},
+358 {'id': {'const': 'second'}}],
+359 'receives': [{'id': {'const': 42}}]}
+360 test(): {'sender': 'test()', 'id': 42,
+361 'content': 'Test Message', 'old': 'content'}
+362 test(): {'sender': 'Test Alias', 'id': 'first',
+363 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+364 test(): {'sender': 'Test Alias', 'id': 'second',
+365 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+366
+367 The "from" key is required:
+368 >>> asyncio.run(controlpi.test(
+369 ... {"Test Alias": {"plugin": "Alias"}}, []))
+370 data must contain ['from'] properties
+371 Configuration for 'Test Alias' is not valid.
+372
+373 The "from" key has to contain a message template and the "to" key a
+374 (partial) message, i.e., both have to be JSON objects:
+375 >>> asyncio.run(controlpi.test(
+376 ... {"Test Alias": {"plugin": "Alias",
+377 ... "from": 42,
+378 ... "to": 42}}, []))
+379 data.from must be object
+380 Configuration for 'Test Alias' is not valid.
+381 >>> asyncio.run(controlpi.test(
+382 ... {"Test Alias": {"plugin": "Alias",
+383 ... "from": {"id": {"const": 42}},
+384 ... "to": 42}}, []))
+385 data.to cannot be validated by any definition
+386 Configuration for 'Test Alias' is not valid.
+387 """
+388
+389 CONF_SCHEMA = {
+390 "properties" : {
+391 "from" : { "type" : "object" },
+392 "to" : {
+393 "anyOf" : [
+394 { "type" : "object" },
+395 { "type" : "array" , "items" : { "type" : "object" }},
+396 ]
+397 },
+398 "translate" : {
+399 "type" : "array" ,
+400 "items" : {
+401 "type" : "object" ,
+402 "properties" : {
+403 "from" : { "type" : "string" },
+404 "to" : { "type" : "string" },
+405 },
+406 },
+407 },
+408 },
+409 "required" : [ "from" ],
+410 }
+411 """Schema for Alias plugin configuration.
+412
+413 Required configuration keys:
+414
+415 - 'from': template of messages to be translated.
+416
+417 Optional configuration keys:
+418
+419 - 'to': translated message(s) to be sent.
+420 - 'translate': array of pairs of keys to be translated.
+421 """
+422
+423 def process_conf ( self ) -> None :
+424 """Register plugin as bus client."""
+425 sends = []
+426 self . _to = []
+427 if "to" in self . conf :
+428 if isinstance ( self . conf [ "to" ], list ):
+429 self . _to = self . conf [ "to" ]
+430 for to in self . conf [ "to" ]:
+431 sends . append ( MessageTemplate . from_message ( to ))
+432 else :
+433 self . _to = [ self . conf [ "to" ]]
+434 sends . append ( MessageTemplate . from_message ( self . conf [ "to" ]))
+435 self . _translate = {}
+436 if "translate" in self . conf :
+437 for pair in self . conf [ "translate" ]:
+438 self . _translate [ pair [ "from" ]] = pair [ "to" ]
+439 self . bus . register (
+440 self . name , "Alias" , sends , [([ self . conf [ "from" ]], self . _alias )]
+441 )
+442
+443 async def _alias ( self , message : Message ) -> None :
+444 # Prevent endless loop:
+445 if message [ "sender" ] != self . name :
+446 for to in self . _to :
+447 alias_message = Message ( self . name , message )
+448 alias_message . update ( to )
+449 for key in self . _translate :
+450 if key in message :
+451 alias_message [ self . _translate [ key ]] = message [ key ]
+452 await self . bus . send ( alias_message )
+453
+454 async def run ( self ) -> None :
+455 """Run no code proactively."""
+456 pass
+457
+458
+459 class Counter ( BasePlugin ):
+460 """Count messages confirming to a given template.
+461
+462 The plugin counts messages confirming to the given template. The
+463 counter can be queried and reset by commands. The 'reset' command also
+464 queries the last count before the reset:
+465 >>> import controlpi
+466 >>> asyncio.run(controlpi.test(
+467 ... {"Test Counter": {"plugin": "Counter",
+468 ... "count": {"id": {"const": 42}}}},
+469 ... [{"target": "Test Counter", "command": "get count"},
+470 ... {"id": 42}, {"id": 42}, {"id": 49},
+471 ... {"target": "Test Counter", "command": "get count"},
+472 ... {"id": 42}, {"id": 42}, {"id": 42},
+473 ... {"target": "Test Counter", "command": "reset"},
+474 ... {"target": "Test Counter", "command": "get count"},
+475 ... {"id": 42}, {"id": 42}, {"id": 42},
+476 ... {"target": "Test Counter", "command": "get count"}]))
+477 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+478 test(): {'sender': '', 'event': 'registered',
+479 'client': 'Test Counter', 'plugin': 'Counter',
+480 'sends': [{'count': {'type': 'integer'}}],
+481 'receives': [{'id': {'const': 42}},
+482 {'target': {'const': 'Test Counter'},
+483 'command': {'const': 'get count'}},
+484 {'target': {'const': 'Test Counter'},
+485 'command': {'const': 'reset'}}]}
+486 test(): {'sender': 'test()', 'target': 'Test Counter',
+487 'command': 'get count'}
+488 test(): {'sender': 'test()', 'id': 42}
+489 test(): {'sender': 'Test Counter', 'count': 0,
+490 'since': ..., 'until': ...}
+491 test(): {'sender': 'test()', 'id': 42}
+492 test(): {'sender': 'test()', 'id': 49}
+493 test(): {'sender': 'test()', 'target': 'Test Counter',
+494 'command': 'get count'}
+495 test(): {'sender': 'test()', 'id': 42}
+496 test(): {'sender': 'Test Counter', 'count': 2,
+497 'since': ..., 'until': ...}
+498 test(): {'sender': 'test()', 'id': 42}
+499 test(): {'sender': 'test()', 'id': 42}
+500 test(): {'sender': 'test()', 'target': 'Test Counter',
+501 'command': 'reset'}
+502 test(): {'sender': 'test()', 'target': 'Test Counter',
+503 'command': 'get count'}
+504 test(): {'sender': 'Test Counter', 'count': 5,
+505 'since': ..., 'until': ...}
+506 test(): {'sender': 'test()', 'id': 42}
+507 test(): {'sender': 'Test Counter', 'count': 0,
+508 'since': ..., 'until': ...}
+509 test(): {'sender': 'test()', 'id': 42}
+510 test(): {'sender': 'test()', 'id': 42}
+511 test(): {'sender': 'test()', 'target': 'Test Counter',
+512 'command': 'get count'}
+513 test(): {'sender': 'Test Counter', 'count': 3,
+514 'since': ..., 'until': ...}
+515 """
+516
+517 CONF_SCHEMA = {
+518 "properties" : {
+519 "count" : { "type" : "object" },
+520 "date format" : { "type" : "string" , "default" : "%Y-%m- %d %H:%M:%S" },
+521 },
+522 "required" : [ "count" ],
+523 }
+524 """Schema for Counter plugin configuration.
+525
+526 Required configuration key:
+527
+528 - 'count': template of messages to be counted.
+529 """
+530
+531 def process_conf ( self ) -> None :
+532 """Register plugin as bus client."""
+533 self . _since = datetime . now () . strftime ( self . conf [ "date format" ])
+534 self . _counter = 0
+535 self . bus . register (
+536 self . name ,
+537 "Counter" ,
+538 [ MessageTemplate ({ "count" : { "type" : "integer" }})],
+539 [
+540 ([ MessageTemplate ( self . conf [ "count" ])], self . _count ),
+541 (
+542 [
+543 MessageTemplate (
+544 {
+545 "target" : { "const" : self . name },
+546 "command" : { "const" : "get count" },
+547 }
+548 )
+549 ],
+550 self . _get_count ,
+551 ),
+552 (
+553 [
+554 MessageTemplate (
+555 {
+556 "target" : { "const" : self . name },
+557 "command" : { "const" : "reset" },
+558 }
+559 )
+560 ],
+561 self . _reset ,
+562 ),
+563 ],
+564 )
+565
+566 async def _count ( self , message : Message ) -> None :
+567 self . _counter += 1
+568
+569 async def _get_count ( self , message : Message ) -> None :
+570 now = datetime . now () . strftime ( self . conf [ "date format" ])
+571 await self . bus . send (
+572 Message (
+573 self . name , { "count" : self . _counter , "since" : self . _since , "until" : now }
+574 )
+575 )
+576
+577 async def _reset ( self , message : Message ) -> None :
+578 now = datetime . now () . strftime ( self . conf [ "date format" ])
+579 counter = self . _counter
+580 self . _counter = 0
+581 await self . bus . send (
+582 Message ( self . name , { "count" : counter , "since" : self . _since , "until" : now })
+583 )
+584 self . _since = now
+585
+586 async def run ( self ) -> None :
+587 """Run no code proactively."""
+588 pass
+589
+590
+591 class Date ( BasePlugin ):
+592 """Send message with current date.
+593
+594 The plugin reacts to 'get date' commands by sending messages with
+595 a 'date' key:
+596 >>> import controlpi
+597 >>> asyncio.run(controlpi.test(
+598 ... {"Test Date": {"plugin": "Date"}},
+599 ... [{"target": "Test Date", "command": "get date"}]))
+600 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+601 test(): {'sender': '', 'event': 'registered',
+602 'client': 'Test Date', 'plugin': 'Date',
+603 'sends': [{'date': {'type': 'string'}}],
+604 'receives': [{'target': {'const': 'Test Date'},
+605 'command': {'const': 'get date'}}]}
+606 test(): {'sender': 'test()', 'target': 'Test Date',
+607 'command': 'get date'}
+608 test(): {'sender': 'Test Date', 'date': ...}
+609
+610 The format of the date can be configured with the 'format'
+611 configuration key:
+612 >>> asyncio.run(controlpi.test(
+613 ... {"Test Date": {"plugin": "Date",
+614 ... "format": "%Y%m%d%H%M%S%f"}},
+615 ... [{"target": "Test Date", "command": "get date"}]))
+616 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+617 test(): {'sender': '', 'event': 'registered',
+618 'client': 'Test Date', 'plugin': 'Date',
+619 'sends': [{'date': {'type': 'string'}}],
+620 'receives': [{'target': {'const': 'Test Date'},
+621 'command': {'const': 'get date'}}]}
+622 test(): {'sender': 'test()', 'target': 'Test Date',
+623 'command': 'get date'}
+624 test(): {'sender': 'Test Date', 'date': ...}
+625 """
+626
+627 CONF_SCHEMA = {
+628 "properties" : { "format" : { "type" : "string" , "default" : "%Y-%m- %d %H:%M:%S" }}
+629 }
+630 """Schema for Date plugin configuration.
+631
+632 Optional configuration key:
+633
+634 - 'format': format for the sent datetime string.
+635 Default: '%Y-%m-%d %H:%M:%S'
+636 """
+637
+638 def process_conf ( self ) -> None :
+639 """Register plugin as bus client."""
+640 self . bus . register (
+641 self . name ,
+642 "Date" ,
+643 [ MessageTemplate ({ "date" : { "type" : "string" }})],
+644 [
+645 (
+646 [
+647 MessageTemplate (
+648 {
+649 "target" : { "const" : self . name },
+650 "command" : { "const" : "get date" },
+651 }
+652 )
+653 ],
+654 self . _date ,
+655 )
+656 ],
+657 )
+658
+659 async def _date ( self , message : Message ) -> None :
+660 date = datetime . now () . strftime ( self . conf [ "format" ])
+661 await self . bus . send ( Message ( self . name , { "date" : date }))
+662
+663 async def run ( self ) -> None :
+664 """Run no code proactively."""
+665 pass
+
+
+
+
+
+
+
+
+ 50 class Log ( BasePlugin ):
+ 51 """Log messages on stdout.
+ 52
+ 53 The "filter" configuration key gets a list of message templates defining
+ 54 the messages that should be logged by the plugin instance.
+ 55
+ 56 In the following example the first and third message match the given
+ 57 template and are logged by the instance "Test Log", while the second
+ 58 message does not match and is only logged by the test, but not by the
+ 59 Log instance:
+ 60 >>> import controlpi
+ 61 >>> asyncio.run(controlpi.test(
+ 62 ... {"Test Log": {"plugin": "Log",
+ 63 ... "filter": [{"id": {"const": 42}}]}},
+ 64 ... [{"id": 42, "message": "Test Message"},
+ 65 ... {"id": 42.42, "message": "Second Message"},
+ 66 ... {"id": 42, "message": "Third Message"}]))
+ 67 ... # doctest: +NORMALIZE_WHITESPACE
+ 68 test(): {'sender': '', 'event': 'registered',
+ 69 'client': 'Test Log', 'plugin': 'Log',
+ 70 'sends': [], 'receives': [{'id': {'const': 42}}]}
+ 71 test(): {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
+ 72 Test Log: {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
+ 73 test(): {'sender': 'test()', 'id': 42.42, 'message': 'Second Message'}
+ 74 test(): {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
+ 75 Test Log: {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
+ 76
+ 77 The "filter" key is required:
+ 78 >>> asyncio.run(controlpi.test(
+ 79 ... {"Test Log": {"plugin": "Log"}}, []))
+ 80 data must contain ['filter'] properties
+ 81 Configuration for 'Test Log' is not valid.
+ 82
+ 83 The "filter" key has to contain a list of message templates, i.e.,
+ 84 JSON objects:
+ 85 >>> asyncio.run(controlpi.test(
+ 86 ... {"Test Log": {"plugin": "Log",
+ 87 ... "filter": [42]}}, []))
+ 88 data.filter[0] must be object
+ 89 Configuration for 'Test Log' is not valid.
+ 90 """
+ 91
+ 92 CONF_SCHEMA = {
+ 93 "properties" : { "filter" : { "type" : "array" , "items" : { "type" : "object" }}},
+ 94 "required" : [ "filter" ],
+ 95 }
+ 96 """Schema for Log plugin configuration.
+ 97
+ 98 Required configuration key:
+ 99
+100 - 'filter': list of message templates to be logged.
+101 """
+102
+103 def process_conf ( self ) -> None :
+104 """Register plugin as bus client."""
+105 self . bus . register ( self . name , "Log" , [], [( self . conf [ "filter" ], self . _log )])
+106
+107 async def _log ( self , message : Message ) -> None :
+108 print ( f " { self . name } : { message } " )
+109
+110 async def run ( self ) -> None :
+111 """Run no code proactively."""
+112 pass
+
+
+
+ Log messages on stdout.
+
+
The "filter" configuration key gets a list of message templates defining
+the messages that should be logged by the plugin instance.
+
+
In the following example the first and third message match the given
+template and are logged by the instance "Test Log", while the second
+message does not match and is only logged by the test, but not by the
+Log instance:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test Log" : { "plugin" : "Log" ,
+... "filter" : [{ "id" : { "const" : 42 }}]}},
+... [{ "id" : 42 , "message" : "Test Message" },
+... { "id" : 42.42 , "message" : "Second Message" },
+... { "id" : 42 , "message" : "Third Message" }]))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Log', 'plugin': 'Log',
+ 'sends': [], 'receives': [{'id': {'const': 42}}]}
+test(): {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
+Test Log: {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
+test(): {'sender': 'test()', 'id': 42.42, 'message': 'Second Message'}
+test(): {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
+Test Log: {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
+
+
+
+
The "filter" key is required:
+
+
+
>>> asyncio . run ( controlpi.test (
+... { "Test Log" : { "plugin" : "Log" }}, []))
+data must contain ['filter'] properties
+Configuration for 'Test Log' is not valid.
+
+
+
+
The "filter" key has to contain a list of message templates, i.e.,
+JSON objects:
+
+
+
>>> asyncio . run ( controlpi.test (
+... { "Test Log" : { "plugin" : "Log" ,
+... "filter" : [ 42 ]}}, []))
+data.filter[0] must be object
+Configuration for 'Test Log' is not valid.
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+{'properties': {'filter': {'type': 'array', 'items': {'type': 'object'}}}, 'required': ['filter']}
+
+
+
+
+
+
Schema for Log plugin configuration.
+
+
Required configuration key:
+
+
+'filter': list of message templates to be logged.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
103 def process_conf ( self ) -> None :
+104 """Register plugin as bus client."""
+105 self . bus . register ( self . name , "Log" , [], [( self . conf [ "filter" ], self . _log )])
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
110 async def run ( self ) -> None :
+111 """Run no code proactively."""
+112 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 115 class Init ( BasePlugin ):
+116 """Send list of messages on startup and on demand.
+117
+118 The "messages" configuration key gets a list of messages to be sent on
+119 startup. The same list is sent in reaction to a message with
+120 "target": NAME and "command": "execute".
+121
+122 In the example, the two configured messages are sent twice, once at
+123 startup and a second time in reaction to the "execute" command sent by
+124 the test:
+125 >>> import controlpi
+126 >>> asyncio.run(controlpi.test(
+127 ... {"Test Init": {"plugin": "Init",
+128 ... "messages": [{"id": 42,
+129 ... "content": "Test Message"},
+130 ... {"id": 42.42,
+131 ... "content": "Second Message"}]}},
+132 ... [{"target": "Test Init", "command": "execute"}]))
+133 ... # doctest: +NORMALIZE_WHITESPACE
+134 test(): {'sender': '', 'event': 'registered',
+135 'client': 'Test Init', 'plugin': 'Init',
+136 'sends': [{'id': {'const': 42},
+137 'content': {'const': 'Test Message'}},
+138 {'id': {'const': 42.42},
+139 'content': {'const': 'Second Message'}}],
+140 'receives': [{'target': {'const': 'Test Init'},
+141 'command': {'const': 'execute'}}]}
+142 test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
+143 test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
+144 test(): {'sender': 'test()', 'target': 'Test Init', 'command': 'execute'}
+145 test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
+146 test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
+147
+148 The "messages" key is required:
+149 >>> asyncio.run(controlpi.test(
+150 ... {"Test Init": {"plugin": "Init"}}, []))
+151 data must contain ['messages'] properties
+152 Configuration for 'Test Init' is not valid.
+153
+154 The "messages" key has to contain a list of (partial) messages, i.e.,
+155 JSON objects:
+156 >>> asyncio.run(controlpi.test(
+157 ... {"Test Init": {"plugin": "Init",
+158 ... "messages": [42]}}, []))
+159 data.messages[0] must be object
+160 Configuration for 'Test Init' is not valid.
+161 """
+162
+163 CONF_SCHEMA = {
+164 "properties" : { "messages" : { "type" : "array" , "items" : { "type" : "object" }}},
+165 "required" : [ "messages" ],
+166 }
+167 """Schema for Init plugin configuration.
+168
+169 Required configuration key:
+170
+171 - 'messages': list of messages to be sent.
+172 """
+173
+174 def process_conf ( self ) -> None :
+175 """Register plugin as bus client."""
+176 self . bus . register (
+177 self . name ,
+178 "Init" ,
+179 [
+180 MessageTemplate . from_message ( message )
+181 for message in self . conf [ "messages" ]
+182 ],
+183 [
+184 (
+185 [
+186 MessageTemplate (
+187 {
+188 "target" : { "const" : self . name },
+189 "command" : { "const" : "execute" },
+190 }
+191 )
+192 ],
+193 self . _execute ,
+194 )
+195 ],
+196 )
+197
+198 async def _execute ( self , message : Message ) -> None :
+199 for message in self . conf [ "messages" ]:
+200 await self . bus . send ( Message ( self . name , message ))
+201 # Give immediate reactions to messages opportunity to happen:
+202 await asyncio . sleep ( 0 )
+203
+204 async def run ( self ) -> None :
+205 """Send configured messages on startup."""
+206 for message in self . conf [ "messages" ]:
+207 await self . bus . send ( Message ( self . name , message ))
+
+
+
+ Send list of messages on startup and on demand.
+
+
The "messages" configuration key gets a list of messages to be sent on
+startup. The same list is sent in reaction to a message with
+"target": NAME and "command": "execute".
+
+
In the example, the two configured messages are sent twice, once at
+startup and a second time in reaction to the "execute" command sent by
+the test:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test Init" : { "plugin" : "Init" ,
+... "messages" : [{ "id" : 42 ,
+... "content" : "Test Message" },
+... { "id" : 42.42 ,
+... "content" : "Second Message" }]}},
+... [{ "target" : "Test Init" , "command" : "execute" }]))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Init', 'plugin': 'Init',
+ 'sends': [{'id': {'const': 42},
+ 'content': {'const': 'Test Message'}},
+ {'id': {'const': 42.42},
+ 'content': {'const': 'Second Message'}}],
+ 'receives': [{'target': {'const': 'Test Init'},
+ 'command': {'const': 'execute'}}]}
+test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
+test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
+test(): {'sender': 'test()', 'target': 'Test Init', 'command': 'execute'}
+test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
+test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
+
+
+
+
The "messages" key is required:
+
+
+
>>> asyncio . run ( controlpi.test (
+... { "Test Init" : { "plugin" : "Init" }}, []))
+data must contain ['messages'] properties
+Configuration for 'Test Init' is not valid.
+
+
+
+
The "messages" key has to contain a list of (partial) messages, i.e.,
+JSON objects:
+
+
+
>>> asyncio . run ( controlpi.test (
+... { "Test Init" : { "plugin" : "Init" ,
+... "messages" : [ 42 ]}}, []))
+data.messages[0] must be object
+Configuration for 'Test Init' is not valid.
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+
+ {'properties': {'messages': {'type': 'array', 'items': {'type': 'object'}}}, 'required': ['messages']}
+
+
+
+
+
+
Schema for Init plugin configuration.
+
+
Required configuration key:
+
+
+'messages': list of messages to be sent.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
174 def process_conf ( self ) -> None :
+175 """Register plugin as bus client."""
+176 self . bus . register (
+177 self . name ,
+178 "Init" ,
+179 [
+180 MessageTemplate . from_message ( message )
+181 for message in self . conf [ "messages" ]
+182 ],
+183 [
+184 (
+185 [
+186 MessageTemplate (
+187 {
+188 "target" : { "const" : self . name },
+189 "command" : { "const" : "execute" },
+190 }
+191 )
+192 ],
+193 self . _execute ,
+194 )
+195 ],
+196 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
204 async def run ( self ) -> None :
+205 """Send configured messages on startup."""
+206 for message in self . conf [ "messages" ]:
+207 await self . bus . send ( Message ( self . name , message ))
+
+
+
+
Send configured messages on startup.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 210 class Execute ( BasePlugin ):
+211 """Send configurable list of messages on demand.
+212
+213 An Execute plugin instance receives two kinds of commands.
+214 The "set messages" command has a "messages" key with a list of (partial)
+215 messages, which are sent by the Execute instance in reaction to an
+216 "execute" command.
+217
+218 In the example, the first command sent by the test sets two messages,
+219 which are then sent in reaction to the second command sent by the test:
+220 >>> import controlpi
+221 >>> asyncio.run(controlpi.test(
+222 ... {"Test Execute": {"plugin": "Execute"}},
+223 ... [{"target": "Test Execute", "command": "set messages",
+224 ... "messages": [{"id": 42, "content": "Test Message"},
+225 ... {"id": 42.42, "content": "Second Message"}]},
+226 ... {"target": "Test Execute", "command": "execute"}]))
+227 ... # doctest: +NORMALIZE_WHITESPACE
+228 test(): {'sender': '', 'event': 'registered',
+229 'client': 'Test Execute', 'plugin': 'Execute',
+230 'sends': [{}],
+231 'receives': [{'target': {'const': 'Test Execute'},
+232 'command': {'const': 'set messages'},
+233 'messages': {'type': 'array',
+234 'items': {'type': 'object'}}},
+235 {'target': {'const': 'Test Execute'},
+236 'command': {'const': 'execute'}}]}
+237 test(): {'sender': 'test()', 'target': 'Test Execute',
+238 'command': 'set messages',
+239 'messages': [{'id': 42, 'content': 'Test Message'},
+240 {'id': 42.42, 'content': 'Second Message'}]}
+241 test(): {'sender': 'test()', 'target': 'Test Execute',
+242 'command': 'execute'}
+243 test(): {'sender': 'Test Execute', 'id': 42,
+244 'content': 'Test Message'}
+245 test(): {'sender': 'Test Execute', 'id': 42.42,
+246 'content': 'Second Message'}
+247 """
+248
+249 CONF_SCHEMA = True
+250 """Schema for Execute plugin configuration.
+251
+252 There are no required or optional configuration keys.
+253 """
+254
+255 def process_conf ( self ) -> None :
+256 """Register plugin as bus client."""
+257 self . messages : List [ Message ] = []
+258 self . bus . register (
+259 self . name ,
+260 "Execute" ,
+261 [ MessageTemplate ()],
+262 [
+263 (
+264 [
+265 MessageTemplate (
+266 {
+267 "target" : { "const" : self . name },
+268 "command" : { "const" : "set messages" },
+269 "messages" : {
+270 "type" : "array" ,
+271 "items" : { "type" : "object" },
+272 },
+273 }
+274 )
+275 ],
+276 self . _set_messages ,
+277 ),
+278 (
+279 [
+280 MessageTemplate (
+281 {
+282 "target" : { "const" : self . name },
+283 "command" : { "const" : "execute" },
+284 }
+285 )
+286 ],
+287 self . _execute ,
+288 ),
+289 ],
+290 )
+291
+292 async def _set_messages ( self , message : Message ) -> None :
+293 assert isinstance ( message [ "messages" ], list )
+294 self . messages = list ( message [ "messages" ])
+295
+296 async def _execute ( self , message : Message ) -> None :
+297 for message in self . messages :
+298 await self . bus . send ( Message ( self . name , message ))
+299 # Give immediate reactions to messages opportunity to happen:
+300 await asyncio . sleep ( 0 )
+301
+302 async def run ( self ) -> None :
+303 """Run no code proactively."""
+304 pass
+
+
+
+ Send configurable list of messages on demand.
+
+
An Execute plugin instance receives two kinds of commands.
+The "set messages" command has a "messages" key with a list of (partial)
+messages, which are sent by the Execute instance in reaction to an
+"execute" command.
+
+
In the example, the first command sent by the test sets two messages,
+which are then sent in reaction to the second command sent by the test:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test Execute" : { "plugin" : "Execute" }},
+... [{ "target" : "Test Execute" , "command" : "set messages" ,
+... "messages" : [{ "id" : 42 , "content" : "Test Message" },
+... { "id" : 42.42 , "content" : "Second Message" }]},
+... { "target" : "Test Execute" , "command" : "execute" }]))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Execute', 'plugin': 'Execute',
+ 'sends': [{}],
+ 'receives': [{'target': {'const': 'Test Execute'},
+ 'command': {'const': 'set messages'},
+ 'messages': {'type': 'array',
+ 'items': {'type': 'object'}}},
+ {'target': {'const': 'Test Execute'},
+ 'command': {'const': 'execute'}}]}
+test(): {'sender': 'test()', 'target': 'Test Execute',
+ 'command': 'set messages',
+ 'messages': [{'id': 42, 'content': 'Test Message'},
+ {'id': 42.42, 'content': 'Second Message'}]}
+test(): {'sender': 'test()', 'target': 'Test Execute',
+ 'command': 'execute'}
+test(): {'sender': 'Test Execute', 'id': 42,
+ 'content': 'Test Message'}
+test(): {'sender': 'Test Execute', 'id': 42.42,
+ 'content': 'Second Message'}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+True
+
+
+
+
+
+
Schema for Execute plugin configuration.
+
+
There are no required or optional configuration keys.
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
255 def process_conf ( self ) -> None :
+256 """Register plugin as bus client."""
+257 self . messages : List [ Message ] = []
+258 self . bus . register (
+259 self . name ,
+260 "Execute" ,
+261 [ MessageTemplate ()],
+262 [
+263 (
+264 [
+265 MessageTemplate (
+266 {
+267 "target" : { "const" : self . name },
+268 "command" : { "const" : "set messages" },
+269 "messages" : {
+270 "type" : "array" ,
+271 "items" : { "type" : "object" },
+272 },
+273 }
+274 )
+275 ],
+276 self . _set_messages ,
+277 ),
+278 (
+279 [
+280 MessageTemplate (
+281 {
+282 "target" : { "const" : self . name },
+283 "command" : { "const" : "execute" },
+284 }
+285 )
+286 ],
+287 self . _execute ,
+288 ),
+289 ],
+290 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
302 async def run ( self ) -> None :
+303 """Run no code proactively."""
+304 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 307 class Alias ( BasePlugin ):
+308 """Translate messages to an alias.
+309
+310 The "from" configuration key gets a message template and the
+311 configuration key "to" a (partial) message. The "translate"
+312 configuration key contains pairs of message keys, where the "from"
+313 message key is translated to the "to" message key if present in the
+314 message.
+315
+316 All messages matching the "from" template are received by the Alias
+317 instance and a message translated by adding the keys and values of the
+318 "to" message and the translated key-value pairs according to
+319 "translate" is sent. Keys that are not "sender" and not modified by
+320 "to" or "translate" are retained.
+321
+322 In the example, the two messages sent by the test are translated by the
+323 Alias instance and the translated messages are sent by it preserving
+324 the "content" keys:
+325 >>> import controlpi
+326 >>> asyncio.run(controlpi.test(
+327 ... {"Test Alias": {"plugin": "Alias",
+328 ... "from": {"id": {"const": 42}},
+329 ... "to": {"id": "translated"},
+330 ... "translate": [{'from': "old", "to": "new"}]}},
+331 ... [{"id": 42, "content": "Test Message", "old": "content"},
+332 ... {"id": 42, "content": "Second Message", "old": "content"}]))
+333 ... # doctest: +NORMALIZE_WHITESPACE
+334 test(): {'sender': '', 'event': 'registered',
+335 'client': 'Test Alias', 'plugin': 'Alias',
+336 'sends': [{'id': {'const': 'translated'}}],
+337 'receives': [{'id': {'const': 42}}]}
+338 test(): {'sender': 'test()', 'id': 42,
+339 'content': 'Test Message', 'old': 'content'}
+340 test(): {'sender': 'test()', 'id': 42,
+341 'content': 'Second Message', 'old': 'content'}
+342 test(): {'sender': 'Test Alias', 'id': 'translated',
+343 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+344 test(): {'sender': 'Test Alias', 'id': 'translated',
+345 'content': 'Second Message', 'old': 'content', 'new': 'content'}
+346
+347 An Alias instance can also translate to a list of messages instead of
+348 a single message:
+349 >>> asyncio.run(controlpi.test(
+350 ... {"Test Alias": {"plugin": "Alias",
+351 ... "from": {"id": {"const": 42}},
+352 ... "to": [{"id": "first"}, {"id": "second"}],
+353 ... "translate": [{'from': "old", "to": "new"}]}},
+354 ... [{"id": 42, "content": "Test Message", "old": "content"}]))
+355 ... # doctest: +NORMALIZE_WHITESPACE
+356 test(): {'sender': '', 'event': 'registered',
+357 'client': 'Test Alias', 'plugin': 'Alias',
+358 'sends': [{'id': {'const': 'first'}},
+359 {'id': {'const': 'second'}}],
+360 'receives': [{'id': {'const': 42}}]}
+361 test(): {'sender': 'test()', 'id': 42,
+362 'content': 'Test Message', 'old': 'content'}
+363 test(): {'sender': 'Test Alias', 'id': 'first',
+364 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+365 test(): {'sender': 'Test Alias', 'id': 'second',
+366 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+367
+368 The "from" key is required:
+369 >>> asyncio.run(controlpi.test(
+370 ... {"Test Alias": {"plugin": "Alias"}}, []))
+371 data must contain ['from'] properties
+372 Configuration for 'Test Alias' is not valid.
+373
+374 The "from" key has to contain a message template and the "to" key a
+375 (partial) message, i.e., both have to be JSON objects:
+376 >>> asyncio.run(controlpi.test(
+377 ... {"Test Alias": {"plugin": "Alias",
+378 ... "from": 42,
+379 ... "to": 42}}, []))
+380 data.from must be object
+381 Configuration for 'Test Alias' is not valid.
+382 >>> asyncio.run(controlpi.test(
+383 ... {"Test Alias": {"plugin": "Alias",
+384 ... "from": {"id": {"const": 42}},
+385 ... "to": 42}}, []))
+386 data.to cannot be validated by any definition
+387 Configuration for 'Test Alias' is not valid.
+388 """
+389
+390 CONF_SCHEMA = {
+391 "properties" : {
+392 "from" : { "type" : "object" },
+393 "to" : {
+394 "anyOf" : [
+395 { "type" : "object" },
+396 { "type" : "array" , "items" : { "type" : "object" }},
+397 ]
+398 },
+399 "translate" : {
+400 "type" : "array" ,
+401 "items" : {
+402 "type" : "object" ,
+403 "properties" : {
+404 "from" : { "type" : "string" },
+405 "to" : { "type" : "string" },
+406 },
+407 },
+408 },
+409 },
+410 "required" : [ "from" ],
+411 }
+412 """Schema for Alias plugin configuration.
+413
+414 Required configuration keys:
+415
+416 - 'from': template of messages to be translated.
+417
+418 Optional configuration keys:
+419
+420 - 'to': translated message(s) to be sent.
+421 - 'translate': array of pairs of keys to be translated.
+422 """
+423
+424 def process_conf ( self ) -> None :
+425 """Register plugin as bus client."""
+426 sends = []
+427 self . _to = []
+428 if "to" in self . conf :
+429 if isinstance ( self . conf [ "to" ], list ):
+430 self . _to = self . conf [ "to" ]
+431 for to in self . conf [ "to" ]:
+432 sends . append ( MessageTemplate . from_message ( to ))
+433 else :
+434 self . _to = [ self . conf [ "to" ]]
+435 sends . append ( MessageTemplate . from_message ( self . conf [ "to" ]))
+436 self . _translate = {}
+437 if "translate" in self . conf :
+438 for pair in self . conf [ "translate" ]:
+439 self . _translate [ pair [ "from" ]] = pair [ "to" ]
+440 self . bus . register (
+441 self . name , "Alias" , sends , [([ self . conf [ "from" ]], self . _alias )]
+442 )
+443
+444 async def _alias ( self , message : Message ) -> None :
+445 # Prevent endless loop:
+446 if message [ "sender" ] != self . name :
+447 for to in self . _to :
+448 alias_message = Message ( self . name , message )
+449 alias_message . update ( to )
+450 for key in self . _translate :
+451 if key in message :
+452 alias_message [ self . _translate [ key ]] = message [ key ]
+453 await self . bus . send ( alias_message )
+454
+455 async def run ( self ) -> None :
+456 """Run no code proactively."""
+457 pass
+
+
+
+ Translate messages to an alias.
+
+
The "from" configuration key gets a message template and the
+configuration key "to" a (partial) message. The "translate"
+configuration key contains pairs of message keys, where the "from"
+message key is translated to the "to" message key if present in the
+message.
+
+
All messages matching the "from" template are received by the Alias
+instance and a message translated by adding the keys and values of the
+"to" message and the translated key-value pairs according to
+"translate" is sent. Keys that are not "sender" and not modified by
+"to" or "translate" are retained.
+
+
In the example, the two messages sent by the test are translated by the
+Alias instance and the translated messages are sent by it preserving
+the "content" keys:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test Alias" : { "plugin" : "Alias" ,
+... "from" : { "id" : { "const" : 42 }},
+... "to" : { "id" : "translated" },
+... "translate" : [{ 'from' : "old" , "to" : "new" }]}},
+... [{ "id" : 42 , "content" : "Test Message" , "old" : "content" },
+... { "id" : 42 , "content" : "Second Message" , "old" : "content" }]))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Alias', 'plugin': 'Alias',
+ 'sends': [{'id': {'const': 'translated'}}],
+ 'receives': [{'id': {'const': 42}}]}
+test(): {'sender': 'test()', 'id': 42,
+ 'content': 'Test Message', 'old': 'content'}
+test(): {'sender': 'test()', 'id': 42,
+ 'content': 'Second Message', 'old': 'content'}
+test(): {'sender': 'Test Alias', 'id': 'translated',
+ 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+test(): {'sender': 'Test Alias', 'id': 'translated',
+ 'content': 'Second Message', 'old': 'content', 'new': 'content'}
+
+
+
+
An Alias instance can also translate to a list of messages instead of
+a single message:
+
+
+
>>> asyncio . run ( controlpi.test (
+... { "Test Alias" : { "plugin" : "Alias" ,
+... "from" : { "id" : { "const" : 42 }},
+... "to" : [{ "id" : "first" }, { "id" : "second" }],
+... "translate" : [{ 'from' : "old" , "to" : "new" }]}},
+... [{ "id" : 42 , "content" : "Test Message" , "old" : "content" }]))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Alias', 'plugin': 'Alias',
+ 'sends': [{'id': {'const': 'first'}},
+ {'id': {'const': 'second'}}],
+ 'receives': [{'id': {'const': 42}}]}
+test(): {'sender': 'test()', 'id': 42,
+ 'content': 'Test Message', 'old': 'content'}
+test(): {'sender': 'Test Alias', 'id': 'first',
+ 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+test(): {'sender': 'Test Alias', 'id': 'second',
+ 'content': 'Test Message', 'old': 'content', 'new': 'content'}
+
+
+
+
The "from" key is required:
+
+
+
>>> asyncio . run ( controlpi.test (
+... { "Test Alias" : { "plugin" : "Alias" }}, []))
+data must contain ['from'] properties
+Configuration for 'Test Alias' is not valid.
+
+
+
+
The "from" key has to contain a message template and the "to" key a
+(partial) message, i.e., both have to be JSON objects:
+
+
+
>>> asyncio . run ( controlpi.test (
+... { "Test Alias" : { "plugin" : "Alias" ,
+... "from" : 42 ,
+... "to" : 42 }}, []))
+data.from must be object
+Configuration for 'Test Alias' is not valid.
+>>> asyncio . run ( controlpi.test (
+... { "Test Alias" : { "plugin" : "Alias" ,
+... "from" : { "id" : { "const" : 42 }},
+... "to" : 42 }}, []))
+data.to cannot be validated by any definition
+Configuration for 'Test Alias' is not valid.
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+
+ {'properties': {'from': {'type': 'object'}, 'to': {'anyOf': [{'type': 'object'}, {'type': 'array', 'items': {'type': 'object'}}]}, 'translate': {'type': 'array', 'items': {'type': 'object', 'properties': {'from': {'type': 'string'}, 'to': {'type': 'string'}}}}}, 'required': ['from']}
+
+
+
+
+
+
Schema for Alias plugin configuration.
+
+
Required configuration keys:
+
+
+'from': template of messages to be translated.
+
+
+
Optional configuration keys:
+
+
+'to': translated message(s) to be sent.
+'translate': array of pairs of keys to be translated.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
424 def process_conf ( self ) -> None :
+425 """Register plugin as bus client."""
+426 sends = []
+427 self . _to = []
+428 if "to" in self . conf :
+429 if isinstance ( self . conf [ "to" ], list ):
+430 self . _to = self . conf [ "to" ]
+431 for to in self . conf [ "to" ]:
+432 sends . append ( MessageTemplate . from_message ( to ))
+433 else :
+434 self . _to = [ self . conf [ "to" ]]
+435 sends . append ( MessageTemplate . from_message ( self . conf [ "to" ]))
+436 self . _translate = {}
+437 if "translate" in self . conf :
+438 for pair in self . conf [ "translate" ]:
+439 self . _translate [ pair [ "from" ]] = pair [ "to" ]
+440 self . bus . register (
+441 self . name , "Alias" , sends , [([ self . conf [ "from" ]], self . _alias )]
+442 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
455 async def run ( self ) -> None :
+456 """Run no code proactively."""
+457 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 460 class Counter ( BasePlugin ):
+461 """Count messages confirming to a given template.
+462
+463 The plugin counts messages confirming to the given template. The
+464 counter can be queried and reset by commands. The 'reset' command also
+465 queries the last count before the reset:
+466 >>> import controlpi
+467 >>> asyncio.run(controlpi.test(
+468 ... {"Test Counter": {"plugin": "Counter",
+469 ... "count": {"id": {"const": 42}}}},
+470 ... [{"target": "Test Counter", "command": "get count"},
+471 ... {"id": 42}, {"id": 42}, {"id": 49},
+472 ... {"target": "Test Counter", "command": "get count"},
+473 ... {"id": 42}, {"id": 42}, {"id": 42},
+474 ... {"target": "Test Counter", "command": "reset"},
+475 ... {"target": "Test Counter", "command": "get count"},
+476 ... {"id": 42}, {"id": 42}, {"id": 42},
+477 ... {"target": "Test Counter", "command": "get count"}]))
+478 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+479 test(): {'sender': '', 'event': 'registered',
+480 'client': 'Test Counter', 'plugin': 'Counter',
+481 'sends': [{'count': {'type': 'integer'}}],
+482 'receives': [{'id': {'const': 42}},
+483 {'target': {'const': 'Test Counter'},
+484 'command': {'const': 'get count'}},
+485 {'target': {'const': 'Test Counter'},
+486 'command': {'const': 'reset'}}]}
+487 test(): {'sender': 'test()', 'target': 'Test Counter',
+488 'command': 'get count'}
+489 test(): {'sender': 'test()', 'id': 42}
+490 test(): {'sender': 'Test Counter', 'count': 0,
+491 'since': ..., 'until': ...}
+492 test(): {'sender': 'test()', 'id': 42}
+493 test(): {'sender': 'test()', 'id': 49}
+494 test(): {'sender': 'test()', 'target': 'Test Counter',
+495 'command': 'get count'}
+496 test(): {'sender': 'test()', 'id': 42}
+497 test(): {'sender': 'Test Counter', 'count': 2,
+498 'since': ..., 'until': ...}
+499 test(): {'sender': 'test()', 'id': 42}
+500 test(): {'sender': 'test()', 'id': 42}
+501 test(): {'sender': 'test()', 'target': 'Test Counter',
+502 'command': 'reset'}
+503 test(): {'sender': 'test()', 'target': 'Test Counter',
+504 'command': 'get count'}
+505 test(): {'sender': 'Test Counter', 'count': 5,
+506 'since': ..., 'until': ...}
+507 test(): {'sender': 'test()', 'id': 42}
+508 test(): {'sender': 'Test Counter', 'count': 0,
+509 'since': ..., 'until': ...}
+510 test(): {'sender': 'test()', 'id': 42}
+511 test(): {'sender': 'test()', 'id': 42}
+512 test(): {'sender': 'test()', 'target': 'Test Counter',
+513 'command': 'get count'}
+514 test(): {'sender': 'Test Counter', 'count': 3,
+515 'since': ..., 'until': ...}
+516 """
+517
+518 CONF_SCHEMA = {
+519 "properties" : {
+520 "count" : { "type" : "object" },
+521 "date format" : { "type" : "string" , "default" : "%Y-%m- %d %H:%M:%S" },
+522 },
+523 "required" : [ "count" ],
+524 }
+525 """Schema for Counter plugin configuration.
+526
+527 Required configuration key:
+528
+529 - 'count': template of messages to be counted.
+530 """
+531
+532 def process_conf ( self ) -> None :
+533 """Register plugin as bus client."""
+534 self . _since = datetime . now () . strftime ( self . conf [ "date format" ])
+535 self . _counter = 0
+536 self . bus . register (
+537 self . name ,
+538 "Counter" ,
+539 [ MessageTemplate ({ "count" : { "type" : "integer" }})],
+540 [
+541 ([ MessageTemplate ( self . conf [ "count" ])], self . _count ),
+542 (
+543 [
+544 MessageTemplate (
+545 {
+546 "target" : { "const" : self . name },
+547 "command" : { "const" : "get count" },
+548 }
+549 )
+550 ],
+551 self . _get_count ,
+552 ),
+553 (
+554 [
+555 MessageTemplate (
+556 {
+557 "target" : { "const" : self . name },
+558 "command" : { "const" : "reset" },
+559 }
+560 )
+561 ],
+562 self . _reset ,
+563 ),
+564 ],
+565 )
+566
+567 async def _count ( self , message : Message ) -> None :
+568 self . _counter += 1
+569
+570 async def _get_count ( self , message : Message ) -> None :
+571 now = datetime . now () . strftime ( self . conf [ "date format" ])
+572 await self . bus . send (
+573 Message (
+574 self . name , { "count" : self . _counter , "since" : self . _since , "until" : now }
+575 )
+576 )
+577
+578 async def _reset ( self , message : Message ) -> None :
+579 now = datetime . now () . strftime ( self . conf [ "date format" ])
+580 counter = self . _counter
+581 self . _counter = 0
+582 await self . bus . send (
+583 Message ( self . name , { "count" : counter , "since" : self . _since , "until" : now })
+584 )
+585 self . _since = now
+586
+587 async def run ( self ) -> None :
+588 """Run no code proactively."""
+589 pass
+
+
+
+ Count messages confirming to a given template.
+
+
The plugin counts messages confirming to the given template. The
+counter can be queried and reset by commands. The 'reset' command also
+queries the last count before the reset:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test Counter" : { "plugin" : "Counter" ,
+... "count" : { "id" : { "const" : 42 }}}},
+... [{ "target" : "Test Counter" , "command" : "get count" },
+... { "id" : 42 }, { "id" : 42 }, { "id" : 49 },
+... { "target" : "Test Counter" , "command" : "get count" },
+... { "id" : 42 }, { "id" : 42 }, { "id" : 42 },
+... { "target" : "Test Counter" , "command" : "reset" },
+... { "target" : "Test Counter" , "command" : "get count" },
+... { "id" : 42 }, { "id" : 42 }, { "id" : 42 },
+... { "target" : "Test Counter" , "command" : "get count" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Counter', 'plugin': 'Counter',
+ 'sends': [{'count': {'type': 'integer'}}],
+ 'receives': [{'id': {'const': 42}},
+ {'target': {'const': 'Test Counter'},
+ 'command': {'const': 'get count'}},
+ {'target': {'const': 'Test Counter'},
+ 'command': {'const': 'reset'}}]}
+test(): {'sender': 'test()', 'target': 'Test Counter',
+ 'command': 'get count'}
+test(): {'sender': 'test()', 'id': 42}
+test(): {'sender': 'Test Counter', 'count': 0,
+ 'since': ..., 'until': ...}
+test(): {'sender': 'test()', 'id': 42}
+test(): {'sender': 'test()', 'id': 49}
+test(): {'sender': 'test()', 'target': 'Test Counter',
+ 'command': 'get count'}
+test(): {'sender': 'test()', 'id': 42}
+test(): {'sender': 'Test Counter', 'count': 2,
+ 'since': ..., 'until': ...}
+test(): {'sender': 'test()', 'id': 42}
+test(): {'sender': 'test()', 'id': 42}
+test(): {'sender': 'test()', 'target': 'Test Counter',
+ 'command': 'reset'}
+test(): {'sender': 'test()', 'target': 'Test Counter',
+ 'command': 'get count'}
+test(): {'sender': 'Test Counter', 'count': 5,
+ 'since': ..., 'until': ...}
+test(): {'sender': 'test()', 'id': 42}
+test(): {'sender': 'Test Counter', 'count': 0,
+ 'since': ..., 'until': ...}
+test(): {'sender': 'test()', 'id': 42}
+test(): {'sender': 'test()', 'id': 42}
+test(): {'sender': 'test()', 'target': 'Test Counter',
+ 'command': 'get count'}
+test(): {'sender': 'Test Counter', 'count': 3,
+ 'since': ..., 'until': ...}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+
+ {'properties': {'count': {'type': 'object'}, 'date format': {'type': 'string', 'default': '%Y-%m-%d %H:%M:%S'}}, 'required': ['count']}
+
+
+
+
+
+
Schema for Counter plugin configuration.
+
+
Required configuration key:
+
+
+'count': template of messages to be counted.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
532 def process_conf ( self ) -> None :
+533 """Register plugin as bus client."""
+534 self . _since = datetime . now () . strftime ( self . conf [ "date format" ])
+535 self . _counter = 0
+536 self . bus . register (
+537 self . name ,
+538 "Counter" ,
+539 [ MessageTemplate ({ "count" : { "type" : "integer" }})],
+540 [
+541 ([ MessageTemplate ( self . conf [ "count" ])], self . _count ),
+542 (
+543 [
+544 MessageTemplate (
+545 {
+546 "target" : { "const" : self . name },
+547 "command" : { "const" : "get count" },
+548 }
+549 )
+550 ],
+551 self . _get_count ,
+552 ),
+553 (
+554 [
+555 MessageTemplate (
+556 {
+557 "target" : { "const" : self . name },
+558 "command" : { "const" : "reset" },
+559 }
+560 )
+561 ],
+562 self . _reset ,
+563 ),
+564 ],
+565 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
587 async def run ( self ) -> None :
+588 """Run no code proactively."""
+589 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 592 class Date ( BasePlugin ):
+593 """Send message with current date.
+594
+595 The plugin reacts to 'get date' commands by sending messages with
+596 a 'date' key:
+597 >>> import controlpi
+598 >>> asyncio.run(controlpi.test(
+599 ... {"Test Date": {"plugin": "Date"}},
+600 ... [{"target": "Test Date", "command": "get date"}]))
+601 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+602 test(): {'sender': '', 'event': 'registered',
+603 'client': 'Test Date', 'plugin': 'Date',
+604 'sends': [{'date': {'type': 'string'}}],
+605 'receives': [{'target': {'const': 'Test Date'},
+606 'command': {'const': 'get date'}}]}
+607 test(): {'sender': 'test()', 'target': 'Test Date',
+608 'command': 'get date'}
+609 test(): {'sender': 'Test Date', 'date': ...}
+610
+611 The format of the date can be configured with the 'format'
+612 configuration key:
+613 >>> asyncio.run(controlpi.test(
+614 ... {"Test Date": {"plugin": "Date",
+615 ... "format": "%Y%m%d%H%M%S%f"}},
+616 ... [{"target": "Test Date", "command": "get date"}]))
+617 ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+618 test(): {'sender': '', 'event': 'registered',
+619 'client': 'Test Date', 'plugin': 'Date',
+620 'sends': [{'date': {'type': 'string'}}],
+621 'receives': [{'target': {'const': 'Test Date'},
+622 'command': {'const': 'get date'}}]}
+623 test(): {'sender': 'test()', 'target': 'Test Date',
+624 'command': 'get date'}
+625 test(): {'sender': 'Test Date', 'date': ...}
+626 """
+627
+628 CONF_SCHEMA = {
+629 "properties" : { "format" : { "type" : "string" , "default" : "%Y-%m- %d %H:%M:%S" }}
+630 }
+631 """Schema for Date plugin configuration.
+632
+633 Optional configuration key:
+634
+635 - 'format': format for the sent datetime string.
+636 Default: '%Y-%m-%d %H:%M:%S'
+637 """
+638
+639 def process_conf ( self ) -> None :
+640 """Register plugin as bus client."""
+641 self . bus . register (
+642 self . name ,
+643 "Date" ,
+644 [ MessageTemplate ({ "date" : { "type" : "string" }})],
+645 [
+646 (
+647 [
+648 MessageTemplate (
+649 {
+650 "target" : { "const" : self . name },
+651 "command" : { "const" : "get date" },
+652 }
+653 )
+654 ],
+655 self . _date ,
+656 )
+657 ],
+658 )
+659
+660 async def _date ( self , message : Message ) -> None :
+661 date = datetime . now () . strftime ( self . conf [ "format" ])
+662 await self . bus . send ( Message ( self . name , { "date" : date }))
+663
+664 async def run ( self ) -> None :
+665 """Run no code proactively."""
+666 pass
+
+
+
+ Send message with current date.
+
+
The plugin reacts to 'get date' commands by sending messages with
+a 'date' key:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test Date" : { "plugin" : "Date" }},
+... [{ "target" : "Test Date" , "command" : "get date" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Date', 'plugin': 'Date',
+ 'sends': [{'date': {'type': 'string'}}],
+ 'receives': [{'target': {'const': 'Test Date'},
+ 'command': {'const': 'get date'}}]}
+test(): {'sender': 'test()', 'target': 'Test Date',
+ 'command': 'get date'}
+test(): {'sender': 'Test Date', 'date': ...}
+
+
+
+
The format of the date can be configured with the 'format'
+configuration key:
+
+
+
>>> asyncio . run ( controlpi.test (
+... { "Test Date" : { "plugin" : "Date" ,
+... "format" : "%Y%m %d %H%M%S %f " }},
+... [{ "target" : "Test Date" , "command" : "get date" }]))
+... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Date', 'plugin': 'Date',
+ 'sends': [{'date': {'type': 'string'}}],
+ 'receives': [{'target': {'const': 'Test Date'},
+ 'command': {'const': 'get date'}}]}
+test(): {'sender': 'test()', 'target': 'Test Date',
+ 'command': 'get date'}
+test(): {'sender': 'Test Date', 'date': ...}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+{'properties': {'format': {'type': 'string', 'default': '%Y-%m-%d %H:%M:%S'}}}
+
+
+
+
+
+
Schema for Date plugin configuration.
+
+
Optional configuration key:
+
+
+'format': format for the sent datetime string.
+Default: '%Y-%m-%d %H:%M:%S'
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
639 def process_conf ( self ) -> None :
+640 """Register plugin as bus client."""
+641 self . bus . register (
+642 self . name ,
+643 "Date" ,
+644 [ MessageTemplate ({ "date" : { "type" : "string" }})],
+645 [
+646 (
+647 [
+648 MessageTemplate (
+649 {
+650 "target" : { "const" : self . name },
+651 "command" : { "const" : "get date" },
+652 }
+653 )
+654 ],
+655 self . _date ,
+656 )
+657 ],
+658 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
664 async def run ( self ) -> None :
+665 """Run no code proactively."""
+666 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/controlpi_plugins/wait.html b/doc/api/controlpi_plugins/wait.html
new file mode 100644
index 0000000..7d5f0c5
--- /dev/null
+++ b/doc/api/controlpi_plugins/wait.html
@@ -0,0 +1,1585 @@
+
+
+
+
+
+
+ controlpi_plugins.wait API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Provide waiting/sleeping plugins for all kinds of systems.
+
+
+Wait waits for time defined in configuration and sends "finished" event.
+GenericWait waits for time defined in "wait" command and sends "finished"
+event with "id" string defined in "wait" command.
+
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test Wait" : { "plugin" : "Wait" , "seconds" : 0.01 },
+... "Test GenericWait" : { "plugin" : "GenericWait" }},
+... [{ "target" : "Test GenericWait" , "command" : "wait" ,
+... "seconds" : 0.02 , "id" : "Long Wait" },
+... { "target" : "Test Wait" , "command" : "wait" }], 0.025 ))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test Wait', 'plugin': 'Wait',
+ 'sends': [{'event': {'const': 'finished'}}],
+ 'receives': [{'target': {'const': 'Test Wait'},
+ 'command': {'const': 'wait'}}]}
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test GenericWait', 'plugin': 'GenericWait',
+ 'sends': [{'event': {'const': 'finished'},
+ 'id': {'type': 'string'}}],
+ 'receives': [{'target': {'const': 'Test GenericWait'},
+ 'command': {'const': 'wait'},
+ 'seconds': {'type': 'number'},
+ 'id': {'type': 'string'}}]}
+test(): {'sender': 'test()', 'target': 'Test GenericWait',
+ 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
+test(): {'sender': 'test()', 'target': 'Test Wait', 'command': 'wait'}
+test(): {'sender': 'Test Wait', 'event': 'finished'}
+test(): {'sender': 'Test GenericWait', 'event': 'finished',
+ 'id': 'Long Wait'}
+
+
+
+
+
+
+ View Source
+
+ 1 """Provide waiting/sleeping plugins for all kinds of systems.
+ 2
+ 3 - Wait waits for time defined in configuration and sends "finished" event.
+ 4 - GenericWait waits for time defined in "wait" command and sends "finished"
+ 5 event with "id" string defined in "wait" command.
+ 6
+ 7 >>> import controlpi
+ 8 >>> asyncio.run(controlpi.test(
+ 9 ... {"Test Wait": {"plugin": "Wait", "seconds": 0.01},
+ 10 ... "Test GenericWait": {"plugin": "GenericWait"}},
+ 11 ... [{"target": "Test GenericWait", "command": "wait",
+ 12 ... "seconds": 0.02, "id": "Long Wait"},
+ 13 ... {"target": "Test Wait", "command": "wait"}], 0.025))
+ 14 ... # doctest: +NORMALIZE_WHITESPACE
+ 15 test(): {'sender': '', 'event': 'registered',
+ 16 'client': 'Test Wait', 'plugin': 'Wait',
+ 17 'sends': [{'event': {'const': 'finished'}}],
+ 18 'receives': [{'target': {'const': 'Test Wait'},
+ 19 'command': {'const': 'wait'}}]}
+ 20 test(): {'sender': '', 'event': 'registered',
+ 21 'client': 'Test GenericWait', 'plugin': 'GenericWait',
+ 22 'sends': [{'event': {'const': 'finished'},
+ 23 'id': {'type': 'string'}}],
+ 24 'receives': [{'target': {'const': 'Test GenericWait'},
+ 25 'command': {'const': 'wait'},
+ 26 'seconds': {'type': 'number'},
+ 27 'id': {'type': 'string'}}]}
+ 28 test(): {'sender': 'test()', 'target': 'Test GenericWait',
+ 29 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
+ 30 test(): {'sender': 'test()', 'target': 'Test Wait', 'command': 'wait'}
+ 31 test(): {'sender': 'Test Wait', 'event': 'finished'}
+ 32 test(): {'sender': 'Test GenericWait', 'event': 'finished',
+ 33 'id': 'Long Wait'}
+ 34 """
+ 35
+ 36 import asyncio
+ 37
+ 38 from controlpi import BasePlugin , Message , MessageTemplate
+ 39
+ 40
+ 41 class Wait ( BasePlugin ):
+ 42 """Wait for time defined in configuration.
+ 43
+ 44 The "seconds" configuration key gets the number of seconds to wait after
+ 45 receiving a "wait" command before sending the "finished" event:
+ 46 >>> import controlpi
+ 47 >>> asyncio.run(controlpi.test(
+ 48 ... {"Long Wait": {"plugin": "Wait", "seconds": 0.02},
+ 49 ... "Short Wait": {"plugin": "Wait", "seconds": 0.01}},
+ 50 ... [{"target": "Long Wait", "command": "wait"},
+ 51 ... {"target": "Short Wait", "command": "wait"}], 0.025))
+ 52 ... # doctest: +NORMALIZE_WHITESPACE
+ 53 test(): {'sender': '', 'event': 'registered',
+ 54 'client': 'Long Wait', 'plugin': 'Wait',
+ 55 'sends': [{'event': {'const': 'finished'}}],
+ 56 'receives': [{'target': {'const': 'Long Wait'},
+ 57 'command': {'const': 'wait'}}]}
+ 58 test(): {'sender': '', 'event': 'registered',
+ 59 'client': 'Short Wait', 'plugin': 'Wait',
+ 60 'sends': [{'event': {'const': 'finished'}}],
+ 61 'receives': [{'target': {'const': 'Short Wait'},
+ 62 'command': {'const': 'wait'}}]}
+ 63 test(): {'sender': 'test()', 'target': 'Long Wait', 'command': 'wait'}
+ 64 test(): {'sender': 'test()', 'target': 'Short Wait', 'command': 'wait'}
+ 65 test(): {'sender': 'Short Wait', 'event': 'finished'}
+ 66 test(): {'sender': 'Long Wait', 'event': 'finished'}
+ 67 """
+ 68
+ 69 CONF_SCHEMA = {
+ 70 "properties" : { "seconds" : { "type" : "number" }},
+ 71 "required" : [ "seconds" ],
+ 72 }
+ 73 """Schema for Wait plugin configuration.
+ 74
+ 75 Required configuration key:
+ 76
+ 77 - 'seconds': number of seconds to wait.
+ 78 """
+ 79
+ 80 def process_conf ( self ) -> None :
+ 81 """Register plugin as bus client."""
+ 82 self . _tasks = set ()
+ 83 self . bus . register (
+ 84 self . name ,
+ 85 "Wait" ,
+ 86 [ MessageTemplate ({ "event" : { "const" : "finished" }})],
+ 87 [
+ 88 (
+ 89 [
+ 90 MessageTemplate (
+ 91 {
+ 92 "target" : { "const" : self . name },
+ 93 "command" : { "const" : "wait" },
+ 94 }
+ 95 )
+ 96 ],
+ 97 self . _wait ,
+ 98 )
+ 99 ],
+100 )
+101
+102 async def _wait ( self , message : Message ) -> None :
+103 async def wait_coroutine ():
+104 await asyncio . sleep ( self . conf [ "seconds" ])
+105 await self . bus . send ( Message ( self . name , { "event" : "finished" }))
+106
+107 # Done in separate task to not block queue awaiting this callback:
+108 task = asyncio . create_task ( wait_coroutine ())
+109 self . _tasks . add ( task )
+110 task . add_done_callback ( self . _tasks . discard )
+111
+112 async def run ( self ) -> None :
+113 """Run no code proactively."""
+114 pass
+115
+116
+117 class GenericWait ( BasePlugin ):
+118 """Wait for time defined in "wait" command.
+119
+120 The "wait" command has message keys "seconds" defining the seconds to
+121 wait and "id" defining a string to be sent back in the "finished" event
+122 after the wait:
+123 >>> import controlpi
+124 >>> asyncio.run(controlpi.test(
+125 ... {"Test GenericWait": {"plugin": "GenericWait"}},
+126 ... [{"target": "Test GenericWait", "command": "wait",
+127 ... "seconds": 0.02, "id": "Long Wait"},
+128 ... {"target": "Test GenericWait", "command": "wait",
+129 ... "seconds": 0.01, "id": "Short Wait"}], 0.025))
+130 ... # doctest: +NORMALIZE_WHITESPACE
+131 test(): {'sender': '', 'event': 'registered',
+132 'client': 'Test GenericWait', 'plugin': 'GenericWait',
+133 'sends': [{'event': {'const': 'finished'},
+134 'id': {'type': 'string'}}],
+135 'receives': [{'target': {'const': 'Test GenericWait'},
+136 'command': {'const': 'wait'},
+137 'seconds': {'type': 'number'},
+138 'id': {'type': 'string'}}]}
+139 test(): {'sender': 'test()', 'target': 'Test GenericWait',
+140 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
+141 test(): {'sender': 'test()', 'target': 'Test GenericWait',
+142 'command': 'wait', 'seconds': 0.01, 'id': 'Short Wait'}
+143 test(): {'sender': 'Test GenericWait', 'event': 'finished',
+144 'id': 'Short Wait'}
+145 test(): {'sender': 'Test GenericWait', 'event': 'finished',
+146 'id': 'Long Wait'}
+147 """
+148
+149 CONF_SCHEMA = True
+150 """Schema for GenericWait plugin configuration.
+151
+152 There are no required or optional configuration keys.
+153 """
+154
+155 def process_conf ( self ) -> None :
+156 """Register plugin as bus client."""
+157 self . _tasks = set ()
+158 self . bus . register (
+159 self . name ,
+160 "GenericWait" ,
+161 [
+162 MessageTemplate (
+163 { "event" : { "const" : "finished" }, "id" : { "type" : "string" }}
+164 )
+165 ],
+166 [
+167 (
+168 [
+169 MessageTemplate (
+170 {
+171 "target" : { "const" : self . name },
+172 "command" : { "const" : "wait" },
+173 "seconds" : { "type" : "number" },
+174 "id" : { "type" : "string" },
+175 }
+176 )
+177 ],
+178 self . _wait ,
+179 )
+180 ],
+181 )
+182
+183 async def _wait ( self , message : Message ) -> None :
+184 async def wait_coroutine ():
+185 assert isinstance ( message [ "seconds" ], float ) or isinstance (
+186 message [ "seconds" ], int
+187 )
+188 await asyncio . sleep ( message [ "seconds" ])
+189 await self . bus . send (
+190 Message ( self . name , { "event" : "finished" , "id" : message [ "id" ]})
+191 )
+192
+193 # Done in separate task to not block queue awaiting this callback:
+194 task = asyncio . create_task ( wait_coroutine ())
+195 self . _tasks . add ( task )
+196 task . add_done_callback ( self . _tasks . discard )
+197
+198 async def run ( self ) -> None :
+199 """Run no code proactively."""
+200 pass
+201
+202
+203 class Timer ( BasePlugin ):
+204 """Timer that can be started and cancelled.
+205
+206 The "seconds" configuration key gets the number of seconds to wait after
+207 receiving a "start" command before sending the "finished" event.
+208 The "cancel" command cancels all outstanding "finished" events and sends
+209 a corresponding "cancelled" event:
+210 >>> import controlpi
+211 >>> asyncio.run(controlpi.test(
+212 ... {"Timer": {"plugin": "Timer", "seconds": 0.01}},
+213 ... [{"target": "Timer", "command": "start"},
+214 ... {"target": "Timer", "command": "start"},
+215 ... {"target": "Timer", "command": "cancel"},
+216 ... {"target": "Timer", "command": "start"},
+217 ... {"target": "Timer", "command": "start"}], 0.015))
+218 ... # doctest: +NORMALIZE_WHITESPACE
+219 test(): {'sender': '', 'event': 'registered',
+220 'client': 'Timer', 'plugin': 'Timer',
+221 'sends': [{'event': {'const': 'finished'}},
+222 {'event': {'const': 'cancelled'}}],
+223 'receives': [{'target': {'const': 'Timer'},
+224 'command': {'const': 'start'}},
+225 {'target': {'const': 'Timer'},
+226 'command': {'const': 'cancel'}}]}
+227 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+228 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+229 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'cancel'}
+230 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+231 test(): {'sender': 'Timer', 'event': 'cancelled', 'count': 2}
+232 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+233 test(): {'sender': 'Timer', 'event': 'finished'}
+234 test(): {'sender': 'Timer', 'event': 'finished'}
+235 """
+236
+237 CONF_SCHEMA = {
+238 "properties" : { "seconds" : { "type" : "number" }},
+239 "required" : [ "seconds" ],
+240 }
+241 """Schema for Timer plugin configuration.
+242
+243 Required configuration key:
+244
+245 - 'seconds': number of seconds to wait.
+246 """
+247
+248 def process_conf ( self ) -> None :
+249 """Register plugin as bus client."""
+250 self . _tasks = set ()
+251 self . started = 0
+252 self . cancelled = 0
+253 self . bus . register (
+254 self . name ,
+255 "Timer" ,
+256 [
+257 MessageTemplate ({ "event" : { "const" : "finished" }}),
+258 MessageTemplate ({ "event" : { "const" : "cancelled" }}),
+259 ],
+260 [
+261 (
+262 [
+263 MessageTemplate (
+264 {
+265 "target" : { "const" : self . name },
+266 "command" : { "const" : "start" },
+267 }
+268 )
+269 ],
+270 self . _start ,
+271 ),
+272 (
+273 [
+274 MessageTemplate (
+275 {
+276 "target" : { "const" : self . name },
+277 "command" : { "const" : "cancel" },
+278 }
+279 )
+280 ],
+281 self . _cancel ,
+282 ),
+283 ],
+284 )
+285
+286 async def _start ( self , message : Message ) -> None :
+287 self . started += 1
+288
+289 async def wait_coroutine ():
+290 await asyncio . sleep ( self . conf [ "seconds" ])
+291 if self . cancelled > 0 :
+292 self . cancelled -= 1
+293 self . started -= 1
+294 elif self . started > 0 :
+295 self . started -= 1
+296 await self . bus . send ( Message ( self . name , { "event" : "finished" }))
+297
+298 # Done in separate task to not block queue awaiting this callback:
+299 task = asyncio . create_task ( wait_coroutine ())
+300 self . _tasks . add ( task )
+301 task . add_done_callback ( self . _tasks . discard )
+302
+303 async def _cancel ( self , message : Message ) -> None :
+304 if self . cancelled < self . started :
+305 cancel = self . started - self . cancelled
+306 self . cancelled = self . started
+307 await self . bus . send (
+308 Message ( self . name , { "event" : "cancelled" , "count" : cancel })
+309 )
+310
+311 async def run ( self ) -> None :
+312 """Run no code proactively."""
+313 pass
+314
+315
+316 class Periodic ( BasePlugin ):
+317 """Send message periodically.
+318
+319 The "seconds" configuration key is the period of the repetition:
+320 receiving a "wait" command before sending the "finished" event:
+321 >>> import controlpi
+322 >>> asyncio.run(controlpi.test(
+323 ... {"Loop": {"plugin": "Periodic", "seconds": 0.01,
+324 ... "message": {"key": "value"}}},
+325 ... [], 0.025))
+326 ... # doctest: +NORMALIZE_WHITESPACE
+327 test(): {'sender': '', 'event': 'registered',
+328 'client': 'Loop', 'plugin': 'Periodic',
+329 'sends': [{'key': {'const': 'value'}}], 'receives': []}
+330 test(): {'sender': 'Loop', 'key': 'value'}
+331 test(): {'sender': 'Loop', 'key': 'value'}
+332 """
+333
+334 CONF_SCHEMA = {
+335 "properties" : { "seconds" : { "type" : "number" }, "message" : { "type" : "object" }},
+336 "required" : [ "seconds" , "message" ],
+337 }
+338 """Schema for Wait plugin configuration.
+339
+340 Required configuration key:
+341
+342 - 'seconds': period of repetition in seconds.
+343 - 'message': message to send periodically.
+344 """
+345
+346 def process_conf ( self ) -> None :
+347 """Register plugin as bus client."""
+348 self . bus . register (
+349 self . name ,
+350 "Periodic" ,
+351 [ MessageTemplate . from_message ( self . conf [ "message" ])],
+352 [],
+353 )
+354
+355 async def run ( self ) -> None :
+356 """Run periodic loop."""
+357 while True :
+358 await asyncio . sleep ( self . conf [ "seconds" ])
+359 await self . bus . send ( Message ( self . name , self . conf [ "message" ]))
+
+
+
+
+
+
+
+
+ 42 class Wait ( BasePlugin ):
+ 43 """Wait for time defined in configuration.
+ 44
+ 45 The "seconds" configuration key gets the number of seconds to wait after
+ 46 receiving a "wait" command before sending the "finished" event:
+ 47 >>> import controlpi
+ 48 >>> asyncio.run(controlpi.test(
+ 49 ... {"Long Wait": {"plugin": "Wait", "seconds": 0.02},
+ 50 ... "Short Wait": {"plugin": "Wait", "seconds": 0.01}},
+ 51 ... [{"target": "Long Wait", "command": "wait"},
+ 52 ... {"target": "Short Wait", "command": "wait"}], 0.025))
+ 53 ... # doctest: +NORMALIZE_WHITESPACE
+ 54 test(): {'sender': '', 'event': 'registered',
+ 55 'client': 'Long Wait', 'plugin': 'Wait',
+ 56 'sends': [{'event': {'const': 'finished'}}],
+ 57 'receives': [{'target': {'const': 'Long Wait'},
+ 58 'command': {'const': 'wait'}}]}
+ 59 test(): {'sender': '', 'event': 'registered',
+ 60 'client': 'Short Wait', 'plugin': 'Wait',
+ 61 'sends': [{'event': {'const': 'finished'}}],
+ 62 'receives': [{'target': {'const': 'Short Wait'},
+ 63 'command': {'const': 'wait'}}]}
+ 64 test(): {'sender': 'test()', 'target': 'Long Wait', 'command': 'wait'}
+ 65 test(): {'sender': 'test()', 'target': 'Short Wait', 'command': 'wait'}
+ 66 test(): {'sender': 'Short Wait', 'event': 'finished'}
+ 67 test(): {'sender': 'Long Wait', 'event': 'finished'}
+ 68 """
+ 69
+ 70 CONF_SCHEMA = {
+ 71 "properties" : { "seconds" : { "type" : "number" }},
+ 72 "required" : [ "seconds" ],
+ 73 }
+ 74 """Schema for Wait plugin configuration.
+ 75
+ 76 Required configuration key:
+ 77
+ 78 - 'seconds': number of seconds to wait.
+ 79 """
+ 80
+ 81 def process_conf ( self ) -> None :
+ 82 """Register plugin as bus client."""
+ 83 self . _tasks = set ()
+ 84 self . bus . register (
+ 85 self . name ,
+ 86 "Wait" ,
+ 87 [ MessageTemplate ({ "event" : { "const" : "finished" }})],
+ 88 [
+ 89 (
+ 90 [
+ 91 MessageTemplate (
+ 92 {
+ 93 "target" : { "const" : self . name },
+ 94 "command" : { "const" : "wait" },
+ 95 }
+ 96 )
+ 97 ],
+ 98 self . _wait ,
+ 99 )
+100 ],
+101 )
+102
+103 async def _wait ( self , message : Message ) -> None :
+104 async def wait_coroutine ():
+105 await asyncio . sleep ( self . conf [ "seconds" ])
+106 await self . bus . send ( Message ( self . name , { "event" : "finished" }))
+107
+108 # Done in separate task to not block queue awaiting this callback:
+109 task = asyncio . create_task ( wait_coroutine ())
+110 self . _tasks . add ( task )
+111 task . add_done_callback ( self . _tasks . discard )
+112
+113 async def run ( self ) -> None :
+114 """Run no code proactively."""
+115 pass
+
+
+
+ Wait for time defined in configuration.
+
+
The "seconds" configuration key gets the number of seconds to wait after
+receiving a "wait" command before sending the "finished" event:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Long Wait" : { "plugin" : "Wait" , "seconds" : 0.02 },
+... "Short Wait" : { "plugin" : "Wait" , "seconds" : 0.01 }},
+... [{ "target" : "Long Wait" , "command" : "wait" },
+... { "target" : "Short Wait" , "command" : "wait" }], 0.025 ))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Long Wait', 'plugin': 'Wait',
+ 'sends': [{'event': {'const': 'finished'}}],
+ 'receives': [{'target': {'const': 'Long Wait'},
+ 'command': {'const': 'wait'}}]}
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Short Wait', 'plugin': 'Wait',
+ 'sends': [{'event': {'const': 'finished'}}],
+ 'receives': [{'target': {'const': 'Short Wait'},
+ 'command': {'const': 'wait'}}]}
+test(): {'sender': 'test()', 'target': 'Long Wait', 'command': 'wait'}
+test(): {'sender': 'test()', 'target': 'Short Wait', 'command': 'wait'}
+test(): {'sender': 'Short Wait', 'event': 'finished'}
+test(): {'sender': 'Long Wait', 'event': 'finished'}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+{'properties': {'seconds': {'type': 'number'}}, 'required': ['seconds']}
+
+
+
+
+
+
Schema for Wait plugin configuration.
+
+
Required configuration key:
+
+
+'seconds': number of seconds to wait.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
81 def process_conf ( self ) -> None :
+ 82 """Register plugin as bus client."""
+ 83 self . _tasks = set ()
+ 84 self . bus . register (
+ 85 self . name ,
+ 86 "Wait" ,
+ 87 [ MessageTemplate ({ "event" : { "const" : "finished" }})],
+ 88 [
+ 89 (
+ 90 [
+ 91 MessageTemplate (
+ 92 {
+ 93 "target" : { "const" : self . name },
+ 94 "command" : { "const" : "wait" },
+ 95 }
+ 96 )
+ 97 ],
+ 98 self . _wait ,
+ 99 )
+100 ],
+101 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
113 async def run ( self ) -> None :
+114 """Run no code proactively."""
+115 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 118 class GenericWait ( BasePlugin ):
+119 """Wait for time defined in "wait" command.
+120
+121 The "wait" command has message keys "seconds" defining the seconds to
+122 wait and "id" defining a string to be sent back in the "finished" event
+123 after the wait:
+124 >>> import controlpi
+125 >>> asyncio.run(controlpi.test(
+126 ... {"Test GenericWait": {"plugin": "GenericWait"}},
+127 ... [{"target": "Test GenericWait", "command": "wait",
+128 ... "seconds": 0.02, "id": "Long Wait"},
+129 ... {"target": "Test GenericWait", "command": "wait",
+130 ... "seconds": 0.01, "id": "Short Wait"}], 0.025))
+131 ... # doctest: +NORMALIZE_WHITESPACE
+132 test(): {'sender': '', 'event': 'registered',
+133 'client': 'Test GenericWait', 'plugin': 'GenericWait',
+134 'sends': [{'event': {'const': 'finished'},
+135 'id': {'type': 'string'}}],
+136 'receives': [{'target': {'const': 'Test GenericWait'},
+137 'command': {'const': 'wait'},
+138 'seconds': {'type': 'number'},
+139 'id': {'type': 'string'}}]}
+140 test(): {'sender': 'test()', 'target': 'Test GenericWait',
+141 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
+142 test(): {'sender': 'test()', 'target': 'Test GenericWait',
+143 'command': 'wait', 'seconds': 0.01, 'id': 'Short Wait'}
+144 test(): {'sender': 'Test GenericWait', 'event': 'finished',
+145 'id': 'Short Wait'}
+146 test(): {'sender': 'Test GenericWait', 'event': 'finished',
+147 'id': 'Long Wait'}
+148 """
+149
+150 CONF_SCHEMA = True
+151 """Schema for GenericWait plugin configuration.
+152
+153 There are no required or optional configuration keys.
+154 """
+155
+156 def process_conf ( self ) -> None :
+157 """Register plugin as bus client."""
+158 self . _tasks = set ()
+159 self . bus . register (
+160 self . name ,
+161 "GenericWait" ,
+162 [
+163 MessageTemplate (
+164 { "event" : { "const" : "finished" }, "id" : { "type" : "string" }}
+165 )
+166 ],
+167 [
+168 (
+169 [
+170 MessageTemplate (
+171 {
+172 "target" : { "const" : self . name },
+173 "command" : { "const" : "wait" },
+174 "seconds" : { "type" : "number" },
+175 "id" : { "type" : "string" },
+176 }
+177 )
+178 ],
+179 self . _wait ,
+180 )
+181 ],
+182 )
+183
+184 async def _wait ( self , message : Message ) -> None :
+185 async def wait_coroutine ():
+186 assert isinstance ( message [ "seconds" ], float ) or isinstance (
+187 message [ "seconds" ], int
+188 )
+189 await asyncio . sleep ( message [ "seconds" ])
+190 await self . bus . send (
+191 Message ( self . name , { "event" : "finished" , "id" : message [ "id" ]})
+192 )
+193
+194 # Done in separate task to not block queue awaiting this callback:
+195 task = asyncio . create_task ( wait_coroutine ())
+196 self . _tasks . add ( task )
+197 task . add_done_callback ( self . _tasks . discard )
+198
+199 async def run ( self ) -> None :
+200 """Run no code proactively."""
+201 pass
+
+
+
+ Wait for time defined in "wait" command.
+
+
The "wait" command has message keys "seconds" defining the seconds to
+wait and "id" defining a string to be sent back in the "finished" event
+after the wait:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Test GenericWait" : { "plugin" : "GenericWait" }},
+... [{ "target" : "Test GenericWait" , "command" : "wait" ,
+... "seconds" : 0.02 , "id" : "Long Wait" },
+... { "target" : "Test GenericWait" , "command" : "wait" ,
+... "seconds" : 0.01 , "id" : "Short Wait" }], 0.025 ))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Test GenericWait', 'plugin': 'GenericWait',
+ 'sends': [{'event': {'const': 'finished'},
+ 'id': {'type': 'string'}}],
+ 'receives': [{'target': {'const': 'Test GenericWait'},
+ 'command': {'const': 'wait'},
+ 'seconds': {'type': 'number'},
+ 'id': {'type': 'string'}}]}
+test(): {'sender': 'test()', 'target': 'Test GenericWait',
+ 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
+test(): {'sender': 'test()', 'target': 'Test GenericWait',
+ 'command': 'wait', 'seconds': 0.01, 'id': 'Short Wait'}
+test(): {'sender': 'Test GenericWait', 'event': 'finished',
+ 'id': 'Short Wait'}
+test(): {'sender': 'Test GenericWait', 'event': 'finished',
+ 'id': 'Long Wait'}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+True
+
+
+
+
+
+
Schema for GenericWait plugin configuration.
+
+
There are no required or optional configuration keys.
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
156 def process_conf ( self ) -> None :
+157 """Register plugin as bus client."""
+158 self . _tasks = set ()
+159 self . bus . register (
+160 self . name ,
+161 "GenericWait" ,
+162 [
+163 MessageTemplate (
+164 { "event" : { "const" : "finished" }, "id" : { "type" : "string" }}
+165 )
+166 ],
+167 [
+168 (
+169 [
+170 MessageTemplate (
+171 {
+172 "target" : { "const" : self . name },
+173 "command" : { "const" : "wait" },
+174 "seconds" : { "type" : "number" },
+175 "id" : { "type" : "string" },
+176 }
+177 )
+178 ],
+179 self . _wait ,
+180 )
+181 ],
+182 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
199 async def run ( self ) -> None :
+200 """Run no code proactively."""
+201 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 204 class Timer ( BasePlugin ):
+205 """Timer that can be started and cancelled.
+206
+207 The "seconds" configuration key gets the number of seconds to wait after
+208 receiving a "start" command before sending the "finished" event.
+209 The "cancel" command cancels all outstanding "finished" events and sends
+210 a corresponding "cancelled" event:
+211 >>> import controlpi
+212 >>> asyncio.run(controlpi.test(
+213 ... {"Timer": {"plugin": "Timer", "seconds": 0.01}},
+214 ... [{"target": "Timer", "command": "start"},
+215 ... {"target": "Timer", "command": "start"},
+216 ... {"target": "Timer", "command": "cancel"},
+217 ... {"target": "Timer", "command": "start"},
+218 ... {"target": "Timer", "command": "start"}], 0.015))
+219 ... # doctest: +NORMALIZE_WHITESPACE
+220 test(): {'sender': '', 'event': 'registered',
+221 'client': 'Timer', 'plugin': 'Timer',
+222 'sends': [{'event': {'const': 'finished'}},
+223 {'event': {'const': 'cancelled'}}],
+224 'receives': [{'target': {'const': 'Timer'},
+225 'command': {'const': 'start'}},
+226 {'target': {'const': 'Timer'},
+227 'command': {'const': 'cancel'}}]}
+228 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+229 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+230 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'cancel'}
+231 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+232 test(): {'sender': 'Timer', 'event': 'cancelled', 'count': 2}
+233 test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+234 test(): {'sender': 'Timer', 'event': 'finished'}
+235 test(): {'sender': 'Timer', 'event': 'finished'}
+236 """
+237
+238 CONF_SCHEMA = {
+239 "properties" : { "seconds" : { "type" : "number" }},
+240 "required" : [ "seconds" ],
+241 }
+242 """Schema for Timer plugin configuration.
+243
+244 Required configuration key:
+245
+246 - 'seconds': number of seconds to wait.
+247 """
+248
+249 def process_conf ( self ) -> None :
+250 """Register plugin as bus client."""
+251 self . _tasks = set ()
+252 self . started = 0
+253 self . cancelled = 0
+254 self . bus . register (
+255 self . name ,
+256 "Timer" ,
+257 [
+258 MessageTemplate ({ "event" : { "const" : "finished" }}),
+259 MessageTemplate ({ "event" : { "const" : "cancelled" }}),
+260 ],
+261 [
+262 (
+263 [
+264 MessageTemplate (
+265 {
+266 "target" : { "const" : self . name },
+267 "command" : { "const" : "start" },
+268 }
+269 )
+270 ],
+271 self . _start ,
+272 ),
+273 (
+274 [
+275 MessageTemplate (
+276 {
+277 "target" : { "const" : self . name },
+278 "command" : { "const" : "cancel" },
+279 }
+280 )
+281 ],
+282 self . _cancel ,
+283 ),
+284 ],
+285 )
+286
+287 async def _start ( self , message : Message ) -> None :
+288 self . started += 1
+289
+290 async def wait_coroutine ():
+291 await asyncio . sleep ( self . conf [ "seconds" ])
+292 if self . cancelled > 0 :
+293 self . cancelled -= 1
+294 self . started -= 1
+295 elif self . started > 0 :
+296 self . started -= 1
+297 await self . bus . send ( Message ( self . name , { "event" : "finished" }))
+298
+299 # Done in separate task to not block queue awaiting this callback:
+300 task = asyncio . create_task ( wait_coroutine ())
+301 self . _tasks . add ( task )
+302 task . add_done_callback ( self . _tasks . discard )
+303
+304 async def _cancel ( self , message : Message ) -> None :
+305 if self . cancelled < self . started :
+306 cancel = self . started - self . cancelled
+307 self . cancelled = self . started
+308 await self . bus . send (
+309 Message ( self . name , { "event" : "cancelled" , "count" : cancel })
+310 )
+311
+312 async def run ( self ) -> None :
+313 """Run no code proactively."""
+314 pass
+
+
+
+ Timer that can be started and cancelled.
+
+
The "seconds" configuration key gets the number of seconds to wait after
+receiving a "start" command before sending the "finished" event.
+The "cancel" command cancels all outstanding "finished" events and sends
+a corresponding "cancelled" event:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Timer" : { "plugin" : "Timer" , "seconds" : 0.01 }},
+... [{ "target" : "Timer" , "command" : "start" },
+... { "target" : "Timer" , "command" : "start" },
+... { "target" : "Timer" , "command" : "cancel" },
+... { "target" : "Timer" , "command" : "start" },
+... { "target" : "Timer" , "command" : "start" }], 0.015 ))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Timer', 'plugin': 'Timer',
+ 'sends': [{'event': {'const': 'finished'}},
+ {'event': {'const': 'cancelled'}}],
+ 'receives': [{'target': {'const': 'Timer'},
+ 'command': {'const': 'start'}},
+ {'target': {'const': 'Timer'},
+ 'command': {'const': 'cancel'}}]}
+test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+test(): {'sender': 'test()', 'target': 'Timer', 'command': 'cancel'}
+test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+test(): {'sender': 'Timer', 'event': 'cancelled', 'count': 2}
+test(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'}
+test(): {'sender': 'Timer', 'event': 'finished'}
+test(): {'sender': 'Timer', 'event': 'finished'}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+{'properties': {'seconds': {'type': 'number'}}, 'required': ['seconds']}
+
+
+
+
+
+
Schema for Timer plugin configuration.
+
+
Required configuration key:
+
+
+'seconds': number of seconds to wait.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
249 def process_conf ( self ) -> None :
+250 """Register plugin as bus client."""
+251 self . _tasks = set ()
+252 self . started = 0
+253 self . cancelled = 0
+254 self . bus . register (
+255 self . name ,
+256 "Timer" ,
+257 [
+258 MessageTemplate ({ "event" : { "const" : "finished" }}),
+259 MessageTemplate ({ "event" : { "const" : "cancelled" }}),
+260 ],
+261 [
+262 (
+263 [
+264 MessageTemplate (
+265 {
+266 "target" : { "const" : self . name },
+267 "command" : { "const" : "start" },
+268 }
+269 )
+270 ],
+271 self . _start ,
+272 ),
+273 (
+274 [
+275 MessageTemplate (
+276 {
+277 "target" : { "const" : self . name },
+278 "command" : { "const" : "cancel" },
+279 }
+280 )
+281 ],
+282 self . _cancel ,
+283 ),
+284 ],
+285 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
312 async def run ( self ) -> None :
+313 """Run no code proactively."""
+314 pass
+
+
+
+
Run no code proactively.
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
+
+ 317 class Periodic ( BasePlugin ):
+318 """Send message periodically.
+319
+320 The "seconds" configuration key is the period of the repetition:
+321 receiving a "wait" command before sending the "finished" event:
+322 >>> import controlpi
+323 >>> asyncio.run(controlpi.test(
+324 ... {"Loop": {"plugin": "Periodic", "seconds": 0.01,
+325 ... "message": {"key": "value"}}},
+326 ... [], 0.025))
+327 ... # doctest: +NORMALIZE_WHITESPACE
+328 test(): {'sender': '', 'event': 'registered',
+329 'client': 'Loop', 'plugin': 'Periodic',
+330 'sends': [{'key': {'const': 'value'}}], 'receives': []}
+331 test(): {'sender': 'Loop', 'key': 'value'}
+332 test(): {'sender': 'Loop', 'key': 'value'}
+333 """
+334
+335 CONF_SCHEMA = {
+336 "properties" : { "seconds" : { "type" : "number" }, "message" : { "type" : "object" }},
+337 "required" : [ "seconds" , "message" ],
+338 }
+339 """Schema for Wait plugin configuration.
+340
+341 Required configuration key:
+342
+343 - 'seconds': period of repetition in seconds.
+344 - 'message': message to send periodically.
+345 """
+346
+347 def process_conf ( self ) -> None :
+348 """Register plugin as bus client."""
+349 self . bus . register (
+350 self . name ,
+351 "Periodic" ,
+352 [ MessageTemplate . from_message ( self . conf [ "message" ])],
+353 [],
+354 )
+355
+356 async def run ( self ) -> None :
+357 """Run periodic loop."""
+358 while True :
+359 await asyncio . sleep ( self . conf [ "seconds" ])
+360 await self . bus . send ( Message ( self . name , self . conf [ "message" ]))
+
+
+
+ Send message periodically.
+
+
The "seconds" configuration key is the period of the repetition:
+receiving a "wait" command before sending the "finished" event:
+
+
+
>>> import controlpi
+>>> asyncio . run ( controlpi.test (
+... { "Loop" : { "plugin" : "Periodic" , "seconds" : 0.01 ,
+... "message" : { "key" : "value" }}},
+... [], 0.025 ))
+... # doctest: +NORMALIZE_WHITESPACE
+test(): {'sender': '', 'event': 'registered',
+ 'client': 'Loop', 'plugin': 'Periodic',
+ 'sends': [{'key': {'const': 'value'}}], 'receives': []}
+test(): {'sender': 'Loop', 'key': 'value'}
+test(): {'sender': 'Loop', 'key': 'value'}
+
+
+
+
+
+
+
+ CONF_SCHEMA =
+
+ {'properties': {'seconds': {'type': 'number'}, 'message': {'type': 'object'}}, 'required': ['seconds', 'message']}
+
+
+
+
+
+
Schema for Wait plugin configuration.
+
+
Required configuration key:
+
+
+'seconds': period of repetition in seconds.
+'message': message to send periodically.
+
+
+
+
+
+
+
+
+
+ def
+ process_conf (self ) -> None :
+
+ View Source
+
+
+
+
347 def process_conf ( self ) -> None :
+348 """Register plugin as bus client."""
+349 self . bus . register (
+350 self . name ,
+351 "Periodic" ,
+352 [ MessageTemplate . from_message ( self . conf [ "message" ])],
+353 [],
+354 )
+
+
+
+
Register plugin as bus client.
+
+
+
+
+
+
+
+
+ async def
+ run (self ) -> None :
+
+ View Source
+
+
+
+
356 async def run ( self ) -> None :
+357 """Run periodic loop."""
+358 while True :
+359 await asyncio . sleep ( self . conf [ "seconds" ])
+360 await self . bus . send ( Message ( self . name , self . conf [ "message" ]))
+
+
+
+
+
+
+
+
+
Inherited Members
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/index.html b/doc/api/index.html
new file mode 100644
index 0000000..fdd4c09
--- /dev/null
+++ b/doc/api/index.html
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+ Module List
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/api/search.js b/doc/api/search.js
new file mode 100644
index 0000000..96f6b7b
--- /dev/null
+++ b/doc/api/search.js
@@ -0,0 +1,46 @@
+window.pdocSearch = (function(){
+/** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oProvide the infrastructure for the ControlPi system.
\n\nThe infrastructure consists of the message bus from module messagebus, the\nplugin registry from module pluginregistry and the abstract base plugin from\nmodule baseplugin.
\n\nThe package combines them in its run function, which is used by __main__.py\nto run a ControlPi system based on a configuration file indefinitely.
\n\nThe test function is a utility function to test plugins with minimal\nboilerplate code.
\n"}, "controlpi.CONF_SCHEMA": {"fullname": "controlpi.CONF_SCHEMA", "modulename": "controlpi", "qualname": "CONF_SCHEMA", "kind": "variable", "doc": "
\n", "default_value": "{'type': 'object', 'patternProperties': {'.*': {'type': 'object'}}}"}, "controlpi.run": {"fullname": "controlpi.run", "modulename": "controlpi", "qualname": "run", "kind": "function", "doc": "Run the ControlPi system based on a configuration.
\n\nSetup message bus, process given configuration, and run message bus and\nplugins concurrently and indefinitely.
\n\nThis function is mainly used by __main__.py to run a ControlPi system\nbased on a configuration loaded from a configuration JSON file on disk.
\n\n\n
>>> async def test_coroutine (): \n... conf = { "Example Init" : \n... { "plugin" : "Init" , \n... "messages" : [{ "id" : 42 , \n... "content" : "Test Message" }, \n... { "id" : 42.42 , \n... "content" : "Second Message" }]}, \n... "Example Log" : \n... { "plugin" : "Log" , \n... "filter" : [{ "sender" : { "const" : "Example Init" }}]}} \n... run_task = asyncio . create_task ( run ( conf )) \n... await asyncio . sleep ( 0.1 ) \n... run_task . cancel () \n... try : \n... await run_task \n... except asyncio . exceptions . CancelledError : \n... pass \n>>> asyncio . run ( test_coroutine ()) # doctest: +NORMALIZE_WHITESPACE \nExample Log: {'sender': 'Example Init', \n 'id': 42, 'content': 'Test Message'} \nExample Log: {'sender': 'Example Init', \n 'id': 42.42, 'content': 'Second Message'} \n\n
\n", "signature": "(conf : Dict [ str , Dict [ str , Any ]] ) -> None : ", "funcdef": "async def"}, "controlpi.test": {"fullname": "controlpi.test", "modulename": "controlpi", "qualname": "test", "kind": "function", "doc": "Test configuration of ControlPi system.
\n\nSetup message bus, process given configuration, run message bus and\nplugins concurrently, send given messages on message bus and print all\nmessages on message bus. Terminate when queue of message bus is empty.
\n\nThis function allows to test single plugins or small plugin\nconfigurations with minimal boilerplate code:
\n\n\n
>>> asyncio . run ( test ( \n... { "Example Init" : { "plugin" : "Init" , \n... "messages" : [{ "id" : 42 , \n... "content" : "Test Message" }, \n... { "id" : 42.42 , \n... "content" : "Second Message" }]}}, \n... [{ "target" : "Example Init" , \n... "command" : "execute" }])) # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Example Init', 'plugin': 'Init', \n 'sends': [{'id': {'const': 42}, \n 'content': {'const': 'Test Message'}}, \n {'id': {'const': 42.42}, \n 'content': {'const': 'Second Message'}}], \n 'receives': [{'target': {'const': 'Example Init'}, \n 'command': {'const': 'execute'}}]} \ntest(): {'sender': 'Example Init', \n 'id': 42, 'content': 'Test Message'} \ntest(): {'sender': 'Example Init', \n 'id': 42.42, 'content': 'Second Message'} \ntest(): {'sender': 'test()', 'target': 'Example Init', \n 'command': 'execute'} \ntest(): {'sender': 'Example Init', \n 'id': 42, 'content': 'Test Message'} \ntest(): {'sender': 'Example Init', \n 'id': 42.42, 'content': 'Second Message'} \n\n
\n\nSimilar functionality could be reached by using the Log and Init plugins\nto print messages and send some messages on the bus, but these would\nclutter the test configuration and code to stop the indefinitely running\nbus would have to be added to each and every test.
\n\nIncorrect plugin configurations can also be tested by this:
\n\n\n
>>> asyncio . run ( test ( \n... { "Example Init" : { "plugin" : "Init" }}, [])) \ndata must contain ['messages'] properties \nConfiguration for 'Example Init' is not valid. \n\n
\n", "signature": "(\tconf : Dict [ str , Dict [ str , Any ]] , \tmessages : List [ Dict [ str , Any ]] , \twait : float = 0.0 ) -> None : ", "funcdef": "async def"}, "controlpi.baseplugin": {"fullname": "controlpi.baseplugin", "modulename": "controlpi.baseplugin", "kind": "module", "doc": "Define base class for all ControlPi plugins.
\n\nThe class BasePlugin provides the abstract base class for concrete plugins\nrunning on the ControlPi system.
\n\nIt has three abstract methods that have to be implemented by all concrete\nplugins:
\n\n\nThe class property CONF_SCHEMA is the JSON schema of the configuration of\nthe plugin. The configuration read from the global configuration file is\nchecked against this schema during initialisation. \nThe method process_conf is called at the end of initialisation and is used\nto initialise the plugin. It can be assumed that self.bus is the message\nbus of the system, self.name the instance name, and self.conf the\nconfiguration already validated against the schema. \nThe run coroutines of all plugins are executed concurrently by the main\nsystem. \n \n\n\n
>>> class TestPlugin ( BasePlugin ): \n... CONF_SCHEMA = { 'properties' : { 'key' : { 'type' : 'string' }}, \n... 'required' : [ 'key' ]} \n... def process_conf ( self ): \n... if 'key' in self . conf : \n... print ( f "Processing ' { self . conf [ 'key' ] } '." ) \n... async def run ( self ): \n... print ( "Doing something else." ) \n\n
\n\nPlugins are configured and run based on the information in the global\nconfiguration. Here, we test this manually:
\n\n\n
>>> import asyncio \n>>> async def test (): \n... p = TestPlugin ( MessageBus (), 'Test Instance' , { 'key' : 'Something' }) \n... await p . run () \n>>> asyncio . run ( test ()) \nProcessing 'Something'. \nDoing something else. \n\n
\n\nEach plugin gets a reference to the system message bus during\ninitialisation, which can be accessed as self.bus in the functions of the\nplugin class. This can be used to register and unregister message bus\nclients:
\n\n\n
>>> class BusPlugin ( BasePlugin ): \n... CONF_SCHEMA = True \n... def process_conf ( self ): \n... self . bus . register ( self . name , 'BusPlugin' , \n... [{ 'event' : { 'type' : 'string' }}], \n... [([{ 'target' : { 'const' : self . name }}], \n... self . _receive )]) \n... async def _receive ( self , message ): \n... print ( f " { self . name } received { message } ." ) \n... await self . bus . send ({ 'sender' : self . name , 'event' : 'Receive' }) \n... async def run ( self ): \n... await self . bus . send ({ 'sender' : self . name , 'event' : 'Run' }) \n\n
\n\nAgain, we run this manually here, but this is done by the main coroutine\nwhen using the system in production:
\n\n\n
>>> async def log ( message ): \n... print ( f "Log: { message } " ) \n>>> async def test_bus_plugin (): \n... bus = MessageBus () \n... p = BusPlugin ( bus , 'Bus Test' , {}) \n... bus . register ( 'Test' , 'TestPlugin' , \n... [{}], [([{ 'sender' : { 'const' : 'Bus Test' }}], log )]) \n... bus_task = asyncio . create_task ( bus . run ()) \n... plugin_task = asyncio . create_task ( p . run ()) \n... await bus . send ({ 'sender' : 'Test' , 'target' : 'Bus Test' , 'key' : 'v' }) \n... await asyncio . sleep ( 0 ) \n... await asyncio . sleep ( 0 ) \n... bus_task . cancel () \n... try : \n... await asyncio . gather ( bus_task , plugin_task ) \n... except asyncio . exceptions . CancelledError : \n... pass \n>>> asyncio . run ( test_bus_plugin ()) \nBus Test received {'sender': 'Test', 'target': 'Bus Test', 'key': 'v'}. \nLog: {'sender': 'Bus Test', 'event': 'Run'} \nLog: {'sender': 'Bus Test', 'event': 'Receive'} \n\n
\n\nOften, there will be a one-to-one correspondence between plugin\ninstances and message bus clients, a plugin instance will be a message bus\nclient. But there are also cases, where one plugin instance might register\nand unregister a lot of message bus clients, maybe even dynamically through\nits lifetime. A plugin for an input/output card might register separate\nclients for each pin of the card, a plugin for some kind of hardware bus\nmight register separate clients for all devices connected to the bus, or a\nnetwork socket plugin might register separate clients for all connections\nto the socket (and unregister them when the connection is closed).
\n"}, "controlpi.baseplugin.JSONSchema": {"fullname": "controlpi.baseplugin.JSONSchema", "modulename": "controlpi.baseplugin", "qualname": "JSONSchema", "kind": "variable", "doc": "
\n", "default_value": "bool | typing.Dict[str, None | str | int | float | bool | typing.Dict[str, typing.Any] | typing.List[typing.Any]]"}, "controlpi.baseplugin.PluginConf": {"fullname": "controlpi.baseplugin.PluginConf", "modulename": "controlpi.baseplugin", "qualname": "PluginConf", "kind": "variable", "doc": "
\n", "default_value": "typing.Dict[str, typing.Any]"}, "controlpi.baseplugin.ConfException": {"fullname": "controlpi.baseplugin.ConfException", "modulename": "controlpi.baseplugin", "qualname": "ConfException", "kind": "class", "doc": "Raise for errors in plugin configurations.
\n", "bases": "builtins.Exception"}, "controlpi.baseplugin.BasePlugin": {"fullname": "controlpi.baseplugin.BasePlugin", "modulename": "controlpi.baseplugin", "qualname": "BasePlugin", "kind": "class", "doc": "Base class for all ControlPi plugins.
\n\n\n
>>> class TestPlugin ( BasePlugin ): \n... CONF_SCHEMA = { 'properties' : { 'key' : { 'type' : 'string' }}, \n... 'required' : [ 'key' ]} \n... def process_conf ( self ): \n... if 'key' in self . conf : \n... print ( f "Processing ' { self . conf [ 'key' ] } '." ) \n... async def run ( self ): \n... print ( "Doing something else." ) \n\n
\n\nInitialisation sets the instance variables bus to the given message bus,\nname to the given name, and conf to the given configuration:
\n\n\n
>>> import asyncio \n>>> class TestPlugin ( BasePlugin ): \n... CONF_SCHEMA = { 'properties' : { 'key' : { 'type' : 'string' }}, \n... 'required' : [ 'key' ]} \n... def process_conf ( self ): \n... if 'key' in self . conf : \n... print ( f "Processing ' { self . conf [ 'key' ] } '." ) \n... async def run ( self ): \n... print ( "Doing something else." ) \n>>> async def test (): \n... p = TestPlugin ( MessageBus (), 'Test Instance' , \n... { 'key' : 'Something' }) \n... print ( p . bus ) \n... print ( p . name ) \n... print ( p . conf ) \n>>> asyncio . run ( test ()) # doctest: +ELLIPSIS \nProcessing 'Something'. \n<controlpi.messagebus.MessageBus object at 0x...> \nTest Instance \n{'key': 'Something'} \n\n
\n\nIt also validates the configuration against the schema in CONF_SCHEMA\nand raises ConfException if is not validated.
\n\n\n
>>> async def test (): \n... p = TestPlugin ( MessageBus (), 'Test Instance' , \n... { 'key' : 42 }) \n>>> asyncio . run ( test ()) # doctest: +NORMALIZE_WHITESPACE \nTraceback (most recent call last): \n ... \nbaseplugin.ConfException : Configuration for 'Test Instance' \nis not valid. \n>>> async def test (): \n... p = TestPlugin ( MessageBus (), 'Test Instance' , \n... { 'key 2' : 'Something' }) \n>>> asyncio . run ( test ()) # doctest: +NORMALIZE_WHITESPACE \nTraceback (most recent call last): \n ... \nbaseplugin.ConfException : Configuration for 'Test Instance' \nis not valid. \n\n
\n\nFinally, it calls process_conf, which is the function that should be\noverridden by concrete plugins.
\n", "bases": "abc.ABC"}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"fullname": "controlpi.baseplugin.BasePlugin.CONF_SCHEMA", "modulename": "controlpi.baseplugin", "qualname": "BasePlugin.CONF_SCHEMA", "kind": "variable", "doc": "
\n", "annotation": ": bool | Dict[str, None | str | int | float | bool | Dict[str, Any] | List[Any]]", "default_value": "False"}, "controlpi.baseplugin.BasePlugin.bus": {"fullname": "controlpi.baseplugin.BasePlugin.bus", "modulename": "controlpi.baseplugin", "qualname": "BasePlugin.bus", "kind": "variable", "doc": "
\n"}, "controlpi.baseplugin.BasePlugin.name": {"fullname": "controlpi.baseplugin.BasePlugin.name", "modulename": "controlpi.baseplugin", "qualname": "BasePlugin.name", "kind": "variable", "doc": "
\n"}, "controlpi.baseplugin.BasePlugin.conf": {"fullname": "controlpi.baseplugin.BasePlugin.conf", "modulename": "controlpi.baseplugin", "qualname": "BasePlugin.conf", "kind": "variable", "doc": "
\n"}, "controlpi.baseplugin.BasePlugin.process_conf": {"fullname": "controlpi.baseplugin.BasePlugin.process_conf", "modulename": "controlpi.baseplugin", "qualname": "BasePlugin.process_conf", "kind": "function", "doc": "Process the configuration.
\n\nAbstract method has to be overridden by concrete plugins.\nprocess_conf is called at the end of initialisation after the bus\nand the configuration are available as self.bus and self.conf, but\nbefore any of the run coroutines are executed.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi.baseplugin.BasePlugin.run": {"fullname": "controlpi.baseplugin.BasePlugin.run", "modulename": "controlpi.baseplugin", "qualname": "BasePlugin.run", "kind": "function", "doc": "Run the plugin.
\n\nThe coroutine is run concurrently with the message bus and all\nother plugins. Initial messages and other tasks can be done here.\nIt is also okay to run a plugin-specific infinite loop concurrently\nwith the rest of the system.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi.messagebus": {"fullname": "controlpi.messagebus", "modulename": "controlpi.messagebus", "kind": "module", "doc": "Provide an asynchronous message bus.
\n\nA message is a dictionary with string keys and string, integer, float,\nBoolean, dictionary, or list values, where the inner dictionaries again\nhave string keys and these values and the inner lists also have elements of\nthese types. All messages have a special key 'sender' with the name of the\nsending client as string value, which is set by the constructor:
\n\n\n
>>> m = Message ( 'Example sender' , { 'key 1' : 'value 1' }) \n>>> m [ 'key 2' ] = 'value 2' \n>>> print ( m ) \n{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'} \n\n
\n\nA message template is a mapping from string keys to JSON schemas as values.\nA message template matches a message if all keys of the template are\ncontained in the message and the values in the message validate against the\nrespective schemas. An empty mapping therefore matches all messages.
\n\nThe bus executes asynchronous callbacks for all messages to be received by\na client. We use a simple callback printing the message in all examples:
\n\n\n
>>> def callback_for_receiver ( receiver ): \n... async def callback ( message ): \n... print ( f " { receiver } : { message } " ) \n... return callback \n\n
\n\nClients can be registered at the bus with a name, lists of message templates\nthey want to use for sending and receiving and a callback function for\nreceiving. An empty list of templates means that the client does not want to\nsend or receive any messages, respectively. A list with an empty template\nmeans that it wants to send arbitrary or receive all messages, respectively:
\n\n\n
>>> async def setup ( bus ): \n... bus . register ( 'Logger' , 'Test Plugin' , \n... [], \n... [([ MessageTemplate ({})], \n... callback_for_receiver ( 'Logger' ))]) \n... bus . register ( 'Client 1' , 'Test Plugin' , \n... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})], \n... [([ MessageTemplate ({ 'target' : { 'const' : 'Client 1' }})], \n... callback_for_receiver ( 'Client 1' ))]) \n\n
\n\nWhile most clients should always use their own name for sending, this is not\nenforced and debugging or management clients could send messages on behalf\nof arbitrary client names.
\n\nThe name of a client has to be unique and is not allowed to be empty\n(otherwise registration fails).
\n\nThe empty name is used to refer to the bus itself. The bus sends messages\nfor registrations and deregistrations of clients containing their complete\ninterface of send and receive templates. This can be used to allow dynamic\n(debug) clients to deal with arbitrary configurations of clients. The bus\nalso reacts to 'get clients' command messages by sending the complete\ninformation of all currently registered clients.
\n\nClients can send to the bus with the send function. Each message has to\ndeclare a sender. The send templates of that sender are checked for a\ntemplate matching the message:
\n\n\n
>>> async def send ( bus ): \n... print ( "Sending messages." ) \n... await bus . send ({ 'sender' : 'Client 1' , 'k1' : 'Test' }) \n... await bus . send ({ 'sender' : '' , 'target' : 'Client 1' }) \n\n
\n\nThe run function executes the message bus forever. If we want to stop it, we\nhave to explicitly cancel the task:
\n\n\n
>>> async def main (): \n... bus = MessageBus () \n... await setup ( bus ) \n... bus_task = asyncio . create_task ( bus . run ()) \n... await send ( bus ) \n... await asyncio . sleep ( 0 ) \n... bus_task . cancel () \n... try : \n... await bus_task \n... except asyncio . exceptions . CancelledError : \n... pass \n>>> asyncio . run ( main ()) # doctest: +NORMALIZE_WHITESPACE \nSending messages. \nLogger: {'sender': '', 'event': 'registered', \n 'client': 'Logger', 'plugin': 'Test Plugin', \n 'sends': [], 'receives': [{}]} \nLogger: {'sender': '', 'event': 'registered', \n 'client': 'Client 1', 'plugin': 'Test Plugin', \n 'sends': [{'k1': {'type': 'string'}}], \n 'receives': [{'target': {'const': 'Client 1'}}]} \nLogger: {'sender': 'Client 1', 'k1': 'Test'} \nLogger: {'sender': '', 'target': 'Client 1'} \nClient 1: {'sender': '', 'target': 'Client 1'} \n\n
\n"}, "controlpi.messagebus.MessageValue": {"fullname": "controlpi.messagebus.MessageValue", "modulename": "controlpi.messagebus", "qualname": "MessageValue", "kind": "variable", "doc": "
\n", "default_value": "None | str | int | float | bool | typing.Dict[str, typing.Any] | typing.List[typing.Any]"}, "controlpi.messagebus.JSONSchema": {"fullname": "controlpi.messagebus.JSONSchema", "modulename": "controlpi.messagebus", "qualname": "JSONSchema", "kind": "variable", "doc": "
\n", "default_value": "bool | typing.Dict[str, None | str | int | float | bool | typing.Dict[str, typing.Any] | typing.List[typing.Any]]"}, "controlpi.messagebus.MessageCallback": {"fullname": "controlpi.messagebus.MessageCallback", "modulename": "controlpi.messagebus", "qualname": "MessageCallback", "kind": "variable", "doc": "
\n", "default_value": "typing.Callable[[ForwardRef('Message')], typing.Coroutine[typing.Any, typing.Any, NoneType]]"}, "controlpi.messagebus.register_schema": {"fullname": "controlpi.messagebus.register_schema", "modulename": "controlpi.messagebus", "qualname": "register_schema", "kind": "function", "doc": "Register the given JSON schema in the global cache.
\n", "signature": "(\tschema : bool | Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]] ) -> bool : ", "funcdef": "def"}, "controlpi.messagebus.validate": {"fullname": "controlpi.messagebus.validate", "modulename": "controlpi.messagebus", "qualname": "validate", "kind": "function", "doc": "Validate the given MessageValue against the given JSON schema string.
\n", "signature": "(\tschema_string : str , \tvalue : None | str | int | float | bool | Dict [ str , Any ] | List [ Any ] ) -> bool : ", "funcdef": "def"}, "controlpi.messagebus.Message": {"fullname": "controlpi.messagebus.Message", "modulename": "controlpi.messagebus", "qualname": "Message", "kind": "class", "doc": "Define arbitrary message.
\n\nMessages are dictionaries with string keys and values that are strings,\nintegers, floats, Booleans, dictionaries that recursively have string\nkeys and values of any of these types, or lists with elements that have\nany of these types. These constraints are checked when setting key-value\npairs of the message.
\n\nA message has to have a sender, which is set by the constructor:
\n\n\n
>>> m = Message ( 'Example sender' ) \n>>> print ( m ) \n{'sender': 'Example sender'} \n\n
\n\nA dictionary can be given to the constructor:
\n\n\n
>>> m = Message ( 'Example sender' , \n... { 'key 1' : 'value 1' , 'key 2' : 'value 2' }) \n>>> print ( m ) \n{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'} \n\n
\n\nA 'sender' set in the initial dictionary is overwritten by the explicitly\ngiven sender in the first argument:
\n\n\n
>>> m = Message ( 'Example sender' , \n... { 'sender' : 'Original sender' , 'key' : 'value' }) \n>>> print ( m ) \n{'sender': 'Example sender', 'key': 'value'} \n\n
\n\nOr the message can be modified after construction:
\n\n\n
>>> m = Message ( 'Example sender' , { 'key 1' : 'value 1' }) \n>>> m [ 'key 2' ] = 'value 2' \n>>> print ( m ) \n{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'} \n\n
\n\nThe 'sender' key can be overwritten, but this should only be done in\nexceptional cases:
\n\n\n
>>> m = Message ( 'Example sender' , { 'key' : 'value' }) \n>>> m [ 'sender' ] = 'New sender' \n>>> print ( m ) \n{'sender': 'New sender', 'key': 'value'} \n\n
\n", "bases": "typing.Dict[str, None | str | int | float | bool | typing.Dict[str, typing.Any] | typing.List[typing.Any]]"}, "controlpi.messagebus.Message.__init__": {"fullname": "controlpi.messagebus.Message.__init__", "modulename": "controlpi.messagebus", "qualname": "Message.__init__", "kind": "function", "doc": "Initialise message.
\n\nMessage is initialised with given sender and possibly given\nkey-value pairs:
\n\n\n
>>> m = Message ( 'Example sender' ) \n>>> print ( m ) \n{'sender': 'Example sender'} \n>>> m = Message ( 'Example sender' , { 'key 1' : 'value 1' }) \n>>> print ( m ) \n{'sender': 'Example sender', 'key 1': 'value 1'} \n\n
\n", "signature": "(\tsender : str , \tinit : Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]] | None = None ) "}, "controlpi.messagebus.Message.check_value": {"fullname": "controlpi.messagebus.Message.check_value", "modulename": "controlpi.messagebus", "qualname": "Message.check_value", "kind": "function", "doc": "Check recursively if a given value is valid.
\n\nNone, strings, integers, floats and Booleans are valid:
\n\n\n
>>> Message . check_value ( None ) \nTrue \n>>> Message . check_value ( 'Spam' ) \nTrue \n>>> Message . check_value ( 42 ) \nTrue \n>>> Message . check_value ( 42.42 ) \nTrue \n>>> Message . check_value ( False ) \nTrue \n\n
\n\nOther basic types are not valid:
\n\n\n
>>> Message . check_value ( b 'bytes' ) \nFalse \n>>> Message . check_value ( 1 j ) \nFalse \n\n
\n\nDictionaries with string keys and recursively valid values are valid:
\n\n\n
>>> Message . check_value ({ 'str value' : 'Spam' , 'int value' : 42 , \n... 'float value' : 42.42 , 'bool value' : False }) \nTrue \n\n
\n\nEmpty dictionaries are valid:
\n\n\n
>>> Message . check_value ({}) \nTrue \n\n
\n\nDictionaries with other keys are not valid:
\n\n\n
>>> Message . check_value ({ 42 : 'int key' }) \nFalse \n\n
\n\nDictionaries with invalid values are not valid:
\n\n\n
>>> Message . check_value ({ 'complex value' : 1 j }) \nFalse \n\n
\n\nLists with valid elements are valid:
\n\n\n
>>> Message . check_value ([ 'Spam' , 42 , 42.42 , False ]) \nTrue \n\n
\n\nEmpty lists are valid:
\n\n\n
>>> Message . check_value ([]) \nTrue \n\n
\n\nLists with invalid elements are not valid:
\n\n\n
>>> Message . check_value ([ 1 j ]) \nFalse \n\n
\n", "signature": "(\tvalue : None | str | int | float | bool | Dict [ str , Any ] | List [ Any ] ) -> bool : ", "funcdef": "def"}, "controlpi.messagebus.Message.update": {"fullname": "controlpi.messagebus.Message.update", "modulename": "controlpi.messagebus", "qualname": "Message.update", "kind": "function", "doc": "Override update to use validity checks.
\n\n\n
>>> m = Message ( 'Example sender' ) \n>>> m . update ({ 'key 1' : 'value 1' , 'key 2' : 'value 2' }) \n>>> print ( m ) \n{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'} \n>>> m . update ({ 42 : 'int key' }) \nTraceback (most recent call last): \n ... \nTypeError : '42' is not a valid key in Message (not a string). \n>>> m . update ({ 'complex value' : 1 j }) \nTraceback (most recent call last): \n ... \nTypeError : '1j' is not a valid value in Message. \n\n
\n\nThis is also used in __init__:
\n\n\n
>>> m = Message ( 'Example sender' , { 'key' : 'value' }) \n>>> print ( m ) \n{'sender': 'Example sender', 'key': 'value'} \n>>> m = Message ( 'Example sender' , { 42 : 'int key' }) \nTraceback (most recent call last): \n ... \nTypeError : '42' is not a valid key in Message (not a string). \n>>> m = Message ( 'Example sender' , { 'complex value' : 1 j }) \nTraceback (most recent call last): \n ... \nTypeError : '1j' is not a valid value in Message. \n\n
\n", "signature": "(self , * args , ** kwargs ) -> None : ", "funcdef": "def"}, "controlpi.messagebus.Message.setdefault": {"fullname": "controlpi.messagebus.Message.setdefault", "modulename": "controlpi.messagebus", "qualname": "Message.setdefault", "kind": "function", "doc": "Override setdefault to use validity checks.
\n\n\n
>>> m = Message ( 'Example sender' ) \n>>> m . setdefault ( 'key' , 'value 1' ) \n'value 1' \n>>> m . setdefault ( 'key' , 'value 2' ) \n'value 1' \n>>> m . setdefault ( 42 , 'int key' ) \nTraceback (most recent call last): \n ... \nTypeError : '42' is not a valid key in Message (not a string). \n>>> m . setdefault ( 'complex value' , 1 j ) \nTraceback (most recent call last): \n ... \nTypeError : '1j' is not a valid value in Message. \n\n
\n\nBut __setitem__ is not called if the key is already present:
\n\n\n
>>> m . setdefault ( 'key' , 1 j ) \n'value 1' \n\n
\n", "signature": "(\tself , \tkey : str , \tvalue : None | str | int | float | bool | Dict [ str , Any ] | List [ Any ] = None ) -> None | str | int | float | bool | Dict [ str , Any ] | List [ Any ] : ", "funcdef": "def"}, "controlpi.messagebus.MessageTemplate": {"fullname": "controlpi.messagebus.MessageTemplate", "modulename": "controlpi.messagebus", "qualname": "MessageTemplate", "kind": "class", "doc": "Define a message template.
\n\nA message template is a mapping from string keys to JSON schemas as\nvalues:
\n\n\n
>>> t = MessageTemplate ({ 'key 1' : { 'const' : 'value' }, \n... 'key 2' : { 'type' : 'string' }}) \n>>> t [ 'key 3' ] = { 'type' : 'object' , \n... 'properties' : { 'key 1' : { 'type' : 'number' }, \n... 'key 2' : True }} \n\n
\n\nA message template matches a message if all keys of the template are\ncontained in the message and the values in the message validate against\nthe respective schemas:
\n\n\n
>>> t . check ( Message ( 'Example Sender' , \n... { 'key 1' : 'value' , 'key 2' : 'some string' , \n... 'key 3' : { 'key 1' : 42 , 'key 2' : None }})) \nTrue \n\n
\n\nAn empty mapping therefore matches all messages:
\n\n\n
>>> t = MessageTemplate () \n>>> t . check ( Message ( 'Example Sender' , { 'arbitrary' : 'content' })) \nTrue \n\n
\n", "bases": "typing.Dict[str, bool | typing.Dict[str, None | str | int | float | bool | typing.Dict[str, typing.Any] | typing.List[typing.Any]]]"}, "controlpi.messagebus.MessageTemplate.__init__": {"fullname": "controlpi.messagebus.MessageTemplate.__init__", "modulename": "controlpi.messagebus", "qualname": "MessageTemplate.__init__", "kind": "function", "doc": "Initialise message.
\n\nTemplate is initialised empty or with given key-value pairs:
\n\n\n
>>> t = MessageTemplate () \n>>> print ( t ) \n{} \n>>> t = MessageTemplate ({ 'key' : { 'const' : 'value' }}) \n>>> print ( t ) \n{'key': {'const': 'value'}} \n\n
\n", "signature": "(\tinit : Dict [ str , bool | Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]]] | None = None ) "}, "controlpi.messagebus.MessageTemplate.from_message": {"fullname": "controlpi.messagebus.MessageTemplate.from_message", "modulename": "controlpi.messagebus", "qualname": "MessageTemplate.from_message", "kind": "function", "doc": "Create template from message.
\n\nTemplate witch constant schemas is created from message:
\n\n\n
>>> m = Message ( 'Example Sender' , { 'key' : 'value' }) \n>>> t = MessageTemplate . from_message ( m ) \n>>> print ( t ) \n{'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}} \n>>> m = Message ( 'Example Sender' , { 'dict' : { 'int' : 42 , 'float' : 42.42 }, \n... 'list' : [ None , True , 'string' ]}) \n>>> t = MessageTemplate . from_message ( m ) \n>>> print ( t ) # doctest: +NORMALIZE_WHITESPACE \n{'sender': {'const': 'Example Sender'}, \n 'dict': {'type': 'object', \n 'properties': {'int': {'const': 42}, \n 'float': {'const': 42.42}}}, \n 'list': {'type': 'array', \n 'items': [{'const': None}, \n {'const': True}, \n {'const': 'string'}]}} \n\n
\n\nThis is especially useful for clients that send certain fully\npredefined messages, where the message is given in the configuration\nand the template for the registration can be constructed by this\nmethod.
\n", "signature": "(\tmessage : controlpi . messagebus . Message ) -> controlpi . messagebus . MessageTemplate : ", "funcdef": "def"}, "controlpi.messagebus.MessageTemplate.update": {"fullname": "controlpi.messagebus.MessageTemplate.update", "modulename": "controlpi.messagebus", "qualname": "MessageTemplate.update", "kind": "function", "doc": "Override update to use validity checks.
\n\n\n
>>> t = MessageTemplate () \n>>> t . update ({ 'key 1' : { 'const' : 'value' }, \n... 'key 2' : { 'type' : 'string' }, \n... 'key 3' : { 'type' : 'object' , \n... 'properties' : { 'key 1' : { 'type' : 'number' }, \n... 'key 2' : True }}}) \n>>> print ( t ) # doctest: +NORMALIZE_WHITESPACE \n{'key 1': {'const': 'value'}, 'key 2': {'type': 'string'}, \n 'key 3': {'type': 'object', \n 'properties': {'key 1': {'type': 'number'}, \n 'key 2': True}}} \n>>> t . update ({ 42 : { 'const' : 'int key' }}) \nTraceback (most recent call last): \n ... \nTypeError : '42' is not a valid key in MessageTemplate (not a string). \n>>> t . update ({ 'key' : 'schema' }) # doctest: +NORMALIZE_WHITESPACE \nTraceback (most recent call last): \n ... \nTypeError : 'schema' is not a valid value in MessageTemplate \n(not a valid JSON schema). \n>>> t . update ({ 'key' : True }) \n\n
\n\nThis is also used in __init__:
\n\n\n
>>> t = MessageTemplate ({ 'key 1' : { 'const' : 'value' }, \n... 'key 2' : { 'type' : 'string' }, \n... 'key 3' : { 'type' : 'object' , \n... 'properties' : { \n... 'key 1' : { 'type' : 'number' }, \n... 'key 2' : True }}}) \n>>> print ( t ) # doctest: +NORMALIZE_WHITESPACE \n{'key 1': {'const': 'value'}, 'key 2': {'type': 'string'}, \n 'key 3': {'type': 'object', \n 'properties': {'key 1': {'type': 'number'}, \n 'key 2': True}}} \n>>> t = MessageTemplate ({ 42 : { 'const' : 'int key' }}) \nTraceback (most recent call last): \n ... \nTypeError : '42' is not a valid key in MessageTemplate (not a string). \n>>> t = MessageTemplate ({ 'key' : 'schema' }) \n... # doctest: +NORMALIZE_WHITESPACE \nTraceback (most recent call last): \n ... \nTypeError : 'schema' is not a valid value in MessageTemplate \n(not a valid JSON schema). \n>>> t = MessageTemplate ({ 'key' : True }) \n\n
\n", "signature": "(self , * args , ** kwargs ) -> None : ", "funcdef": "def"}, "controlpi.messagebus.MessageTemplate.setdefault": {"fullname": "controlpi.messagebus.MessageTemplate.setdefault", "modulename": "controlpi.messagebus", "qualname": "MessageTemplate.setdefault", "kind": "function", "doc": "Override setdefault to use validity checks.
\n\n\n
>>> t = MessageTemplate () \n>>> t . setdefault ( 'key 1' , { 'const' : 'value' }) \n{'const': 'value'} \n>>> t . setdefault ( 'key 2' , { 'type' : 'string' }) \n{'type': 'string'} \n>>> t . setdefault ( 'key 3' , { 'type' : 'object' , \n... 'properties' : { 'key 1' : { 'type' : 'number' }, \n... 'key 2' : True }}) \n... # doctest: +NORMALIZE_WHITESPACE \n{'type': 'object', \n 'properties': {'key 1': {'type': 'number'}, \n 'key 2': True}} \n>>> t . setdefault ( 42 , { 'const' : 'int key' }) \nTraceback (most recent call last): \n ... \nTypeError : '42' is not a valid key in MessageTemplate (not a string). \n>>> t . setdefault ( 'key' , 'schema' ) # doctest: +NORMALIZE_WHITESPACE \nTraceback (most recent call last): \n ... \nTypeError : 'schema' is not a valid value in MessageTemplate \n(not a valid JSON schema). \n\n
\n\nBut __setitem__ is not called if the key is already present:
\n\n\n
>>> t . setdefault ( 'key 1' , 'schema' ) \n{'const': 'value'} \n\n
\n", "signature": "(\tself , \tkey : str , \tvalue : bool | Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]] | None = None ) -> bool | Dict [ str , None | str | int | float | bool | Dict [ str , Any ] | List [ Any ]] : ", "funcdef": "def"}, "controlpi.messagebus.MessageTemplate.check": {"fullname": "controlpi.messagebus.MessageTemplate.check", "modulename": "controlpi.messagebus", "qualname": "MessageTemplate.check", "kind": "function", "doc": "Check message against this template.
\n\nConstant values have to match exactly:
\n\n\n
>>> t = MessageTemplate ({ 'key' : { 'const' : 'value' }}) \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 'value' })) \nTrue \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 'other value' })) \nFalse \n\n
\n\nBut for integers, floats with the same value are also valid:
\n\n\n
>>> t = MessageTemplate ({ 'key' : { 'const' : 42 }}) \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 42 })) \nTrue \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 42.0 })) \nTrue \n\n
\n\nType integer is valid for floats with zero fractional part, but\nnot by floats with non-zero fractional part:
\n\n\n
>>> t = MessageTemplate ({ 'key' : { 'type' : 'integer' }}) \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 42 })) \nTrue \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 42.0 })) \nTrue \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 42.42 })) \nFalse \n\n
\n\nType number is valid for arbitrary ints or floats:
\n\n\n
>>> t = MessageTemplate ({ 'key' : { 'type' : 'number' }}) \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 42 })) \nTrue \n>>> t . check ( Message ( 'Example Sender' , { 'key' : 42.42 })) \nTrue \n\n
\n\nAll keys in template have to be present in message:
\n\n\n
>>> t = MessageTemplate ({ 'key 1' : { 'const' : 'value' }, \n... 'key 2' : { 'type' : 'string' }, \n... 'key 3' : { 'type' : 'object' , \n... 'properties' : { \n... 'key 1' : { 'type' : 'number' }, \n... 'key 2' : True , \n... 'key 3' : False }}}) \n>>> t . check ( Message ( 'Example Sender' , \n... { 'key 1' : 'value' , 'key 2' : 'some string' })) \nFalse \n\n
\n\nBut for nested objects their properties do not necessarily have\nto be present:
\n\n\n
>>> t . check ( Message ( 'Example Sender' , \n... { 'key 1' : 'value' , 'key 2' : 'some string' , \n... 'key 3' : { 'key 1' : 42 }})) \nTrue \n\n
\n\nSchema True matches everything (even None):
\n\n\n
>>> t . check ( Message ( 'Example Sender' , \n... { 'key 1' : 'value' , 'key 2' : 'some string' , \n... 'key 3' : { 'key 2' : None }})) \nTrue \n\n
\n\nSchema False matches nothing:
\n\n\n
>>> t . check ( Message ( 'Example Sender' , \n... { 'key 1' : 'value' , 'key 2' : 'some string' , \n... 'key 3' : { 'key 3' : True }})) \nFalse \n\n
\n\nMessage is valid for the constant template created from it:
\n\n\n
>>> m = Message ( 'Example Sender' , { 'dict' : { 'int' : 42 , 'float' : 42.42 }, \n... 'list' : [ None , True , 'string' ]}) \n>>> t = MessageTemplate . from_message ( m ) \n>>> t . check ( m ) \nTrue \n\n
\n", "signature": "(self , message : controlpi . messagebus . Message ) -> bool : ", "funcdef": "def"}, "controlpi.messagebus.TemplateRegistry": {"fullname": "controlpi.messagebus.TemplateRegistry", "modulename": "controlpi.messagebus", "qualname": "TemplateRegistry", "kind": "class", "doc": "Manage a collection of message templates with registered clients.
\n\nA new TemplateRegistry is created by:
\n\n\n
>>> r = TemplateRegistry () \n\n
\n\nClient names (strings) can be registered for message templates, which\nare mappings from keys to JSON schemas:
\n\n\n
>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'C 1' ) \n\n
\n\nThe check function checks if the templates registered for a client\nmatch a given message:
\n\n\n
>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'C 1' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: True \n{'k1': 'v1', 'k2': 2}: True \n{'k1': 'v2', 'k2': 'v1'}: False \n{'k1': 'v2', 'k2': 2}: False \n\n
\n\nClients can be registered for values validating against arbitrary JSON\nschemas, e.g. all values of a certain type:
\n\n\n
>>> r . insert ({ 'k1' : { 'const' : 'v2' }, 'k2' : { 'type' : 'string' }}, 'C 2' ) \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'C 2' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: False \n{'k1': 'v1', 'k2': 2}: False \n{'k1': 'v2', 'k2': 'v1'}: True \n{'k1': 'v2', 'k2': 2}: False \n>>> r . insert ({ 'k1' : { 'const' : 'v2' }, 'k2' : { 'type' : 'integer' }}, 'C 3' ) \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'C 3' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: False \n{'k1': 'v1', 'k2': 2}: False \n{'k1': 'v2', 'k2': 'v1'}: False \n{'k1': 'v2', 'k2': 2}: True \n\n
\n\nThe order of key-value pairs does not have to match the order in the\nmessages and keys can be left out:
\n\n\n
>>> r . insert ({ 'k2' : { 'const' : 2 }}, 'C 4' ) \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'C 4' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: False \n{'k1': 'v1', 'k2': 2}: True \n{'k1': 'v2', 'k2': 'v1'}: False \n{'k1': 'v2', 'k2': 2}: True \n\n
\n\nA registration for an empty template matches all messages:
\n\n\n
>>> r . insert ({}, 'C 5' ) \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'C 5' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: True \n{'k1': 'v1', 'k2': 2}: True \n{'k1': 'v2', 'k2': 'v1'}: True \n{'k1': 'v2', 'k2': 2}: True \n\n
\n\nA client can be registered for multiple templates:
\n\n\n
>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'C 6' ) \n>>> r . insert ({ 'k2' : { 'const' : 'v1' }}, 'C 6' ) \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'C 6' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: True \n{'k1': 'v1', 'k2': 2}: True \n{'k1': 'v2', 'k2': 'v1'}: True \n{'k1': 'v2', 'k2': 2}: False \n\n
\n\nClients can be deregistered again (the result is False if the registry\nis empty after the deletion):
\n\n\n
>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'C 7' ) \n>>> r . delete ( 'C 7' ) \nTrue \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'C 7' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: False \n{'k1': 'v1', 'k2': 2}: False \n{'k1': 'v2', 'k2': 'v1'}: False \n{'k1': 'v2', 'k2': 2}: False \n\n
\n\nThe get function returns all clients with registered templates matching\na given message:
\n\n\n
>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . get ( m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: ['C 5', 'C 1', 'C 6'] \n{'k1': 'v1', 'k2': 2}: ['C 5', 'C 1', 'C 6', 'C 4'] \n{'k1': 'v2', 'k2': 'v1'}: ['C 5', 'C 2', 'C 6'] \n{'k1': 'v2', 'k2': 2}: ['C 5', 'C 3', 'C 4'] \n\n
\n\nThe get_templates function returns all templates for a given client:
\n\n\n
>>> for c in [ 'C 1' , 'C 2' , 'C 3' , 'C 4' , 'C 5' , 'C 6' ]: \n... print ( f " { c } : { r . get_templates ( c ) } " ) \nC 1: [{'k1': {'const': 'v1'}}] \nC 2: [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}] \nC 3: [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}] \nC 4: [{'k2': {'const': 2}}] \nC 5: [{}] \nC 6: [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}] \n\n
\n"}, "controlpi.messagebus.TemplateRegistry.__init__": {"fullname": "controlpi.messagebus.TemplateRegistry.__init__", "modulename": "controlpi.messagebus", "qualname": "TemplateRegistry.__init__", "kind": "function", "doc": "Initialise an empty registry.
\n\n\n
>>> r = TemplateRegistry () \n\n
\n", "signature": "() "}, "controlpi.messagebus.TemplateRegistry.insert": {"fullname": "controlpi.messagebus.TemplateRegistry.insert", "modulename": "controlpi.messagebus", "qualname": "TemplateRegistry.insert", "kind": "function", "doc": "Register a client for a template.
\n\n\n
>>> r = TemplateRegistry () \n>>> r . insert ({ 'k1' : { 'const' : 'v1' }, 'k2' : { 'type' : 'integer' }}, 'C 1' ) \n>>> r . insert ({ 'k1' : { 'const' : 'v1' }, 'k2' : { 'type' : 'string' }}, 'C 2' ) \n>>> r . insert ({ 'k1' : { 'type' : 'integer' }, 'k2' : { 'const' : 'v1' }}, 'C 3' ) \n>>> r . insert ({ 'k1' : { 'type' : 'integer' }, 'k2' : { 'const' : 'v2' }}, 'C 4' ) \n>>> r . insert ({}, 'C 5' ) \n\n
\n", "signature": "(\tself , \ttemplate : controlpi . messagebus . MessageTemplate , \tclient : str , \tcallback : Callable [[ controlpi . messagebus . Message ], Coroutine [ Any , Any , NoneType ]] | None = None ) -> None : ", "funcdef": "def"}, "controlpi.messagebus.TemplateRegistry.delete": {"fullname": "controlpi.messagebus.TemplateRegistry.delete", "modulename": "controlpi.messagebus", "qualname": "TemplateRegistry.delete", "kind": "function", "doc": "Unregister a client from all templates.
\n\n\n
>>> r = TemplateRegistry () \n>>> r . insert ({ 'k1' : { 'const' : 'v1' }, 'k2' : { 'type' : 'integer' }}, 'C 1' ) \n>>> r . insert ({ 'k1' : { 'const' : 'v1' }, 'k2' : { 'type' : 'string' }}, 'C 2' ) \n>>> r . insert ({ 'k1' : { 'type' : 'integer' }, 'k2' : { 'const' : 'v1' }}, 'C 3' ) \n>>> r . insert ({ 'k1' : { 'type' : 'integer' }, 'k2' : { 'const' : 'v2' }}, 'C 4' ) \n>>> r . insert ({}, 'C 5' ) \n>>> r . delete ( 'C 3' ) \nTrue \n>>> r . delete ( 'C 4' ) \nTrue \n\n
\n", "signature": "(self , client : str ) -> bool : ", "funcdef": "def"}, "controlpi.messagebus.TemplateRegistry.check": {"fullname": "controlpi.messagebus.TemplateRegistry.check", "modulename": "controlpi.messagebus", "qualname": "TemplateRegistry.check", "kind": "function", "doc": "Get if a client has a registered template matching a message.
\n\n\n
>>> r = TemplateRegistry () \n>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'Client 1' ) \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'Client 1' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: True \n{'k1': 'v1', 'k2': 2}: True \n{'k1': 'v2', 'k2': 'v1'}: False \n{'k1': 'v2', 'k2': 2}: False \n>>> r . insert ({ 'k2' : { 'type' : 'integer' }}, 'Client 2' ) \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . check ( 'Client 2' , m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: False \n{'k1': 'v1', 'k2': 2}: True \n{'k1': 'v2', 'k2': 'v1'}: False \n{'k1': 'v2', 'k2': 2}: True \n\n
\n", "signature": "(self , client : str , message : controlpi . messagebus . Message ) -> bool : ", "funcdef": "def"}, "controlpi.messagebus.TemplateRegistry.get": {"fullname": "controlpi.messagebus.TemplateRegistry.get", "modulename": "controlpi.messagebus", "qualname": "TemplateRegistry.get", "kind": "function", "doc": "Get all clients registered for templates matching a message.
\n\n\n
>>> r = TemplateRegistry () \n>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'Client 1' ) \n>>> r . insert ({ 'k2' : { 'type' : 'integer' }}, 'Client 2' ) \n>>> for m in [{ 'k1' : 'v1' , 'k2' : 'v1' }, { 'k1' : 'v1' , 'k2' : 2 }, \n... { 'k1' : 'v2' , 'k2' : 'v1' }, { 'k1' : 'v2' , 'k2' : 2 }]: \n... print ( f " { m } : { r . get ( m ) } " ) \n{'k1': 'v1', 'k2': 'v1'}: ['Client 1'] \n{'k1': 'v1', 'k2': 2}: ['Client 1', 'Client 2'] \n{'k1': 'v2', 'k2': 'v1'}: [] \n{'k1': 'v2', 'k2': 2}: ['Client 2'] \n\n
\n", "signature": "(self , message : controlpi . messagebus . Message ) -> List [ str ] : ", "funcdef": "def"}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"fullname": "controlpi.messagebus.TemplateRegistry.get_callbacks", "modulename": "controlpi.messagebus", "qualname": "TemplateRegistry.get_callbacks", "kind": "function", "doc": "Get all callbacks registered for templates matching a message.
\n", "signature": "(\tself , \tmessage : controlpi . messagebus . Message ) -> List [ Callable [[ controlpi . messagebus . Message ], Coroutine [ Any , Any , NoneType ]]] : ", "funcdef": "def"}, "controlpi.messagebus.TemplateRegistry.get_templates": {"fullname": "controlpi.messagebus.TemplateRegistry.get_templates", "modulename": "controlpi.messagebus", "qualname": "TemplateRegistry.get_templates", "kind": "function", "doc": "Get all templates for a client.
\n\n\n
>>> r = TemplateRegistry () \n>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'Client 1' ) \n>>> r . get_templates ( 'Client 1' ) \n[{'k1': {'const': 'v1'}}] \n>>> r . insert ({ 'k1' : { 'const' : 'v2' }, \n... 'k2' : { 'type' : 'string' }}, 'Client 2' ) \n>>> r . get_templates ( 'Client 2' ) \n[{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}] \n>>> r . insert ({ 'k1' : { 'const' : 'v2' }, \n... 'k2' : { 'type' : 'integer' }}, 'Client 3' ) \n>>> r . get_templates ( 'Client 3' ) \n[{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}] \n>>> r . insert ({ 'k2' : { 'const' : 2 }}, 'Client 4' ) \n>>> r . get_templates ( 'Client 4' ) \n[{'k2': {'const': 2}}] \n>>> r . insert ({}, 'Client 5' ) \n>>> r . get_templates ( 'Client 5' ) \n[{}] \n>>> r . insert ({ 'k1' : { 'const' : 'v1' }}, 'Client 6' ) \n>>> r . insert ({ 'k2' : { 'const' : 'v1' }}, 'Client 6' ) \n>>> r . get_templates ( 'Client 6' ) \n[{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}] \n\n
\n", "signature": "(self , client : str ) -> List [ controlpi . messagebus . MessageTemplate ] : ", "funcdef": "def"}, "controlpi.messagebus.BusException": {"fullname": "controlpi.messagebus.BusException", "modulename": "controlpi.messagebus", "qualname": "BusException", "kind": "class", "doc": "Raise for errors in using message bus.
\n", "bases": "builtins.Exception"}, "controlpi.messagebus.MessageBus": {"fullname": "controlpi.messagebus.MessageBus", "modulename": "controlpi.messagebus", "qualname": "MessageBus", "kind": "class", "doc": "Provide an asynchronous message bus.
\n\nThe bus executes asynchronous callbacks for all messages to be received\nby a client. We use a simple callback printing the message in all\nexamples:
\n\n\n
>>> def callback_for_receiver ( receiver ): \n... print ( f "Creating callback for { receiver } ." ) \n... async def callback ( message ): \n... print ( f " { receiver } : { message } " ) \n... return callback \n\n
\n\nClients can be registered at the bus with a name, lists of message\ntemplates they want to use for sending and receiving and a callback\nfunction for receiving. An empty list of templates means that the\nclient does not want to send or receive any messages, respectively.\nA list with an empty template means that it wants to send arbitrary\nor receive all messages, respectively:
\n\n\n
>>> async def setup ( bus ): \n... print ( "Setting up." ) \n... bus . register ( 'Logger' , 'Test Plugin' , \n... [], \n... [([ MessageTemplate ({})], \n... callback_for_receiver ( 'Logger' ))]) \n... bus . register ( 'Client 1' , 'Test Plugin' , \n... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})], \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 1' }})], \n... callback_for_receiver ( 'Client 1' ))]) \n... bus . register ( 'Client 2' , 'Test Plugin' , \n... [ MessageTemplate ({})], \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 2' }})], \n... callback_for_receiver ( 'Client 2' ))]) \n\n
\n\nThe bus itself is addressed by the empty string. It sends messages for\neach registration and deregestration of a client with a key 'event' and\na value of 'registered' or 'unregistered', a key 'client' with the\nclient's name as value and for registrations also keys 'sends' and\n'receives' with all templates registered for the client for sending and\nreceiving.
\n\nClients can send to the bus with the send function. Each message has to\ndeclare a sender. The send templates of that sender are checked for a\ntemplate matching the message. We cannot prevent arbitrary code from\nimpersonating any sender, but this should only be done in debugging or\nmanagement situations.
\n\nMessages that are intended for a specific client by convention have a\nkey 'target' with the target client's name as value. Such messages are\noften commands to the client to do something, which is by convention\nindicated by a key 'command' with a value that indicates what should be\ndone.
\n\nThe bus, for example, reacts to a message with 'target': '' and\n'command': 'get clients' by sending one message for each currently\nregistered with complete information about its registered send and\nreceive templates.
\n\n\n
>>> async def send ( bus ): \n... print ( "Sending messages." ) \n... await bus . send ({ 'sender' : 'Client 1' , 'k1' : 'Test' }) \n... await bus . send ({ 'sender' : 'Client 2' , 'target' : 'Client 1' }) \n... await bus . send ({ 'sender' : '' , 'target' : '' , \n... 'command' : 'get clients' }) \n\n
\n\nThe run function executes the message bus forever. If we want to stop\nit, we have to explicitly cancel the task:
\n\n\n
>>> async def main (): \n... bus = MessageBus () \n... await setup ( bus ) \n... bus_task = asyncio . create_task ( bus . run ()) \n... await send ( bus ) \n... await asyncio . sleep ( 0 ) \n... bus_task . cancel () \n... try : \n... await bus_task \n... except asyncio . exceptions . CancelledError : \n... pass \n>>> asyncio . run ( main ()) # doctest: +NORMALIZE_WHITESPACE \nSetting up. \nCreating callback for Logger. \nCreating callback for Client 1. \nCreating callback for Client 2. \nSending messages. \nLogger: {'sender': '', 'event': 'registered', \n 'client': 'Logger', 'plugin': 'Test Plugin', \n 'sends': [], 'receives': [{}]} \nLogger: {'sender': '', 'event': 'registered', \n 'client': 'Client 1', 'plugin': 'Test Plugin', \n 'sends': [{'k1': {'type': 'string'}}], \n 'receives': [{'target': {'const': 'Client 1'}}]} \nLogger: {'sender': '', 'event': 'registered', \n 'client': 'Client 2', 'plugin': 'Test Plugin', \n 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]} \nLogger: {'sender': 'Client 1', 'k1': 'Test'} \nLogger: {'sender': 'Client 2', 'target': 'Client 1'} \nClient 1: {'sender': 'Client 2', 'target': 'Client 1'} \nLogger: {'sender': '', 'target': '', 'command': 'get clients'} \nLogger: {'sender': '', 'client': 'Logger', 'plugin': 'Test Plugin', \n 'sends': [], 'receives': [{}]} \nLogger: {'sender': '', 'client': 'Client 1', 'plugin': 'Test Plugin', \n 'sends': [{'k1': {'type': 'string'}}], \n 'receives': [{'target': {'const': 'Client 1'}}]} \nLogger: {'sender': '', 'client': 'Client 2', 'plugin': 'Test Plugin', \n 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]} \n\n
\n"}, "controlpi.messagebus.MessageBus.__init__": {"fullname": "controlpi.messagebus.MessageBus.__init__", "modulename": "controlpi.messagebus", "qualname": "MessageBus.__init__", "kind": "function", "doc": "Initialise a new bus without clients.
\n\n\n
>>> async def main (): \n... bus = MessageBus () \n>>> asyncio . run ( main ()) \n\n
\n", "signature": "() "}, "controlpi.messagebus.MessageBus.register": {"fullname": "controlpi.messagebus.MessageBus.register", "modulename": "controlpi.messagebus", "qualname": "MessageBus.register", "kind": "function", "doc": "Register a client at the message bus.
\n\n\n
>>> async def callback ( message ): \n... print ( message ) \n>>> async def main (): \n... bus = MessageBus () \n... bus . register ( 'Logger' , 'Test Plugin' , \n... [], # send nothing \n... [([ MessageTemplate ({})], # receive everything \n... callback )]) \n... bus . register ( 'Client 1' , 'Test Plugin' , \n... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})], \n... # send with key 'k1' and string value \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 1' }})], \n... # receive for this client \n... callback )]) \n... bus . register ( 'Client 2' , 'Test Plugin' , \n... [ MessageTemplate ({})], # send arbitrary \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 2' }})], \n... # receive for this client \n... callback )]) \n>>> asyncio . run ( main ()) \n\n
\n", "signature": "(\tself , \tclient : str , \tplugin : str , \tsends : Iterable [ controlpi . messagebus . MessageTemplate ] , \treceives : Iterable [ Tuple [ Iterable [ controlpi . messagebus . MessageTemplate ], Callable [[ controlpi . messagebus . Message ], Coroutine [ Any , Any , NoneType ]]]] ) -> None : ", "funcdef": "def"}, "controlpi.messagebus.MessageBus.unregister": {"fullname": "controlpi.messagebus.MessageBus.unregister", "modulename": "controlpi.messagebus", "qualname": "MessageBus.unregister", "kind": "function", "doc": "Unregister a client from the message bus.
\n\n\n
>>> async def callback ( message ): \n... print ( message ) \n>>> async def main (): \n... bus = MessageBus () \n... bus . register ( 'Client 1' , 'Test Plugin' , \n... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})], \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 1' }})], \n... callback )]) \n... bus . unregister ( 'Client 1' ) \n>>> asyncio . run ( main ()) \n\n
\n", "signature": "(self , client : str ) -> None : ", "funcdef": "def"}, "controlpi.messagebus.MessageBus.run": {"fullname": "controlpi.messagebus.MessageBus.run", "modulename": "controlpi.messagebus", "qualname": "MessageBus.run", "kind": "function", "doc": "Run the message bus forever.
\n\n\n
>>> async def main (): \n... bus = MessageBus () \n... bus_task = asyncio . create_task ( bus . run ()) \n... bus_task . cancel () \n... try : \n... await bus_task \n... except asyncio . exceptions . CancelledError : \n... pass \n>>> asyncio . run ( main ()) \n\n
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi.messagebus.MessageBus.send": {"fullname": "controlpi.messagebus.MessageBus.send", "modulename": "controlpi.messagebus", "qualname": "MessageBus.send", "kind": "function", "doc": "Send a message to the message bus.
\n\n\n
>>> async def callback ( message ): \n... print ( f "Got: { message } " ) \n>>> async def main (): \n... bus = MessageBus () \n... bus . register ( 'Client 1' , 'Test Plugin' , \n... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})], \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 1' }})], \n... callback )]) \n... bus . register ( 'Client 2' , 'Test Plugin' , \n... [ MessageTemplate ({})], \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 2' }})], \n... callback )]) \n... bus_task = asyncio . create_task ( bus . run ()) \n... await bus . send ({ 'sender' : 'Client 1' , 'target' : 'Client 2' , \n... 'k1' : 'Test' }) \n... await bus . send ({ 'sender' : 'Client 2' , 'target' : 'Client 1' }) \n... try : \n... await bus . send ({ 'sender' : 'Client 1' , 'target' : 'Client 2' , \n... 'k1' : 42 }) \n... except BusException as e : \n... print ( e ) \n... await asyncio . sleep ( 0 ) \n... bus_task . cancel () \n... try : \n... await bus_task \n... except asyncio . exceptions . CancelledError : \n... pass \n>>> asyncio . run ( main ()) # doctest: +NORMALIZE_WHITESPACE \nMessage '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}' \nnot allowed for sender 'Client 1'. \nGot: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'} \nGot: {'sender': 'Client 2', 'target': 'Client 1'} \n\n
\n", "signature": "(self , message : controlpi . messagebus . Message ) -> None : ", "funcdef": "async def"}, "controlpi.messagebus.MessageBus.send_nowait": {"fullname": "controlpi.messagebus.MessageBus.send_nowait", "modulename": "controlpi.messagebus", "qualname": "MessageBus.send_nowait", "kind": "function", "doc": "Send a message to the message bus without blocking.
\n\n\n
>>> async def callback ( message ): \n... print ( f "Got: { message } " ) \n>>> async def main (): \n... bus = MessageBus () \n... bus . register ( 'Client 1' , 'Test Plugin' , \n... [ MessageTemplate ({ 'k1' : { 'type' : 'string' }})], \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 1' }})], \n... callback )]) \n... bus . register ( 'Client 2' , 'Test Plugin' , \n... [ MessageTemplate ({})], \n... [([ MessageTemplate ({ 'target' : \n... { 'const' : 'Client 2' }})], \n... callback )]) \n... bus_task = asyncio . create_task ( bus . run ()) \n... bus . send_nowait ({ 'sender' : 'Client 1' , 'target' : 'Client 2' , \n... 'k1' : 'Test' }) \n... bus . send_nowait ({ 'sender' : 'Client 2' , 'target' : 'Client 1' }) \n... try : \n... bus . send_nowait ({ 'sender' : 'Client 1' , \n... 'target' : 'Client 2' , 'k1' : 42 }) \n... except BusException as e : \n... print ( e ) \n... await asyncio . sleep ( 0 ) \n... bus_task . cancel () \n... try : \n... await bus_task \n... except asyncio . exceptions . CancelledError : \n... pass \n>>> asyncio . run ( main ()) # doctest: +NORMALIZE_WHITESPACE \nMessage '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}' \nnot allowed for sender 'Client 1'. \nGot: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'} \nGot: {'sender': 'Client 2', 'target': 'Client 1'} \n\n
\n", "signature": "(self , message : controlpi . messagebus . Message ) -> None : ", "funcdef": "def"}, "controlpi.pluginregistry": {"fullname": "controlpi.pluginregistry", "modulename": "controlpi.pluginregistry", "kind": "module", "doc": "Provide a generic plugin system.
\n\nThe class PluginRegistry is initialised with the name of a namespace\npackage and a base class.
\n\nAll modules in the namespace package are loaded. These modules can be\nincluded in different distribution packages, which allows to dynamically\nadd plugins to the system without changing any code.
\n\nAfterwards, all (direct and indirect) subclasses of the base class are\nregistered as plugins under their class name. Class names should be unique,\nwhich cannot be programmatically enforced.
\n\n\n
>>> class BasePlugin : \n... pass \n>>> class Plugin1 ( BasePlugin ): \n... pass \n>>> class Plugin2 ( BasePlugin ): \n... pass \n>>> registry = PluginRegistry ( 'importlib' , BasePlugin ) \n\n
\n\nThe registry provides a generic mapping interface with the class names as\nkeys and the classes as values.
\n\n\n
>>> print ( len ( registry )) \n2 \n>>> for name in registry : \n... print ( f " { name } : { registry [ name ] } " ) \nPlugin1: <class 'pluginregistry.Plugin1'> \nPlugin2: <class 'pluginregistry.Plugin2'> \n>>> if 'Plugin1' in registry : \n... print ( f "'Plugin1' is in registry." ) \n'Plugin1' is in registry. \n>>> p1 = registry [ 'Plugin1' ] \n>>> i1 = p1 () \n\n
\n"}, "controlpi.pluginregistry.PluginRegistry": {"fullname": "controlpi.pluginregistry.PluginRegistry", "modulename": "controlpi.pluginregistry", "qualname": "PluginRegistry", "kind": "class", "doc": "Provide a registry for plugins.
\n\nInitialise the registry by loading all modules in the given namespace\npackage and then registering all subclasses of the given base class as\nplugins (only simulated here \u2013 the code for Plugin1 and Plugin2 should\nbe in modules in the given namespace package in real applications):
\n\n\n
>>> class BasePlugin : \n... pass \n>>> class Plugin1 ( BasePlugin ): \n... pass \n>>> class Plugin2 ( BasePlugin ): \n... pass \n>>> registry = PluginRegistry ( 'importlib' , BasePlugin ) \n\n
\n\nAfter initialisation, provide a mapping interface to the plugins:
\n\n\n
>>> print ( len ( registry )) \n2 \n>>> for name in registry : \n... print ( f " { name } : { registry [ name ] } " ) \nPlugin1: <class 'pluginregistry.Plugin1'> \nPlugin2: <class 'pluginregistry.Plugin2'> \n>>> if 'Plugin1' in registry : \n... print ( f "'Plugin1' is in registry." ) \n'Plugin1' is in registry. \n\n
\n", "bases": "collections.abc.Mapping"}, "controlpi.pluginregistry.PluginRegistry.__init__": {"fullname": "controlpi.pluginregistry.PluginRegistry.__init__", "modulename": "controlpi.pluginregistry", "qualname": "PluginRegistry.__init__", "kind": "function", "doc": "Initialise registry.
\n\nImport all modules defined in the given namespace package (in any\ndistribution package currently installed in the path). Then register\nall subclasses of the given base class as plugins.
\n\n\n
>>> class BasePlugin : \n... pass \n>>> class Plugin1 ( BasePlugin ): \n... pass \n>>> class Plugin2 ( BasePlugin ): \n... pass \n>>> registry = PluginRegistry ( 'importlib' , BasePlugin ) \n>>> for name in registry . _plugins : \n... print ( f " { name } : { registry . _plugins [ name ] } " ) \nPlugin1: <class 'pluginregistry.Plugin1'> \nPlugin2: <class 'pluginregistry.Plugin2'> \n\n
\n", "signature": "(namespace_package : str , base_class : type ) "}, "controlpi_plugins": {"fullname": "controlpi_plugins", "modulename": "controlpi_plugins", "kind": "module", "doc": "
\n"}, "controlpi_plugins.state": {"fullname": "controlpi_plugins.state", "modulename": "controlpi_plugins.state", "kind": "module", "doc": "Provide state plugins for all kinds of systems.
\n\n\nState represents a Boolean state. \nStateAlias translates to another state-like client. \nAndState combines several state-like clients by conjunction. \nOrState combines several state-like clients by disjunction. \nAndSet sets a state due to a conjunction of other state-like clients. \nOrSet sets a state due to a disjunction of other state-like clients. \n \n\nAll these plugins use the following conventions:
\n\n\nIf their state changes they send a message containing \"event\": \"changed\"\nand \"state\": NEW STATE. \nIf their state is reported due to a message, but did not change they send\na message containing just \"state\": CURRENT STATE. \nIf they receive a message containing \"target\": NAME and\n\"command\": \"get state\" they report their current state. \nIf State (or any other settable state using these conventions) receives\na message containing \"target\": NAME, \"command\": \"set state\" and\n\"new state\": STATE TO SET it changes the state accordingly. If this\nwas really a change the corresponding event is sent. If it was already in\nthis state a report message without \"event\": \"changed\" is sent. \nStateAlias can alias any message bus client using these conventions, not\njust State instances. It translates all messages described here in both\ndirections. \nAndState and OrState instances cannot be set. \nAndState and OrState can combine any message bus clients using these\nconventions, not just State instances. They only react to messages\ncontaining \"state\" information. \n \n\n\n
>>> import asyncio \n>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test State" : { "plugin" : "State" }, \n... "Test State 2" : { "plugin" : "State" }, \n... "Test State 3" : { "plugin" : "State" }, \n... "Test State 4" : { "plugin" : "State" }, \n... "Test StateAlias" : { "plugin" : "StateAlias" , \n... "alias for" : "Test State 2" }, \n... "Test AndState" : { "plugin" : "AndState" , \n... "states" : [ "Test State" , "Test StateAlias" ]}, \n... "Test OrState" : { "plugin" : "OrState" , \n... "states" : [ "Test State" , "Test StateAlias" ]}, \n... "Test AndSet" : { "plugin" : "AndSet" , \n... "input states" : [ "Test State" , "Test StateAlias" ], \n... "output state" : "Test State 3" }, \n... "Test OrSet" : { "plugin" : "OrSet" , \n... "input states" : [ "Test State" , "Test StateAlias" ], \n... "output state" : "Test State 4" }}, \n... [{ "target" : "Test AndState" , \n... "command" : "get state" }, \n... { "target" : "Test OrState" , \n... "command" : "get state" }, \n... { "target" : "Test State" , \n... "command" : "set state" , "new state" : True }, \n... { "target" : "Test StateAlias" , \n... "command" : "set state" , "new state" : True }, \n... { "target" : "Test State" , \n... "command" : "set state" , "new state" : False }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', ... \ntest(): {'sender': 'test()', 'target': 'Test AndState', 'command': 'get state'} \ntest(): {'sender': 'test()', 'target': 'Test OrState', 'command': 'get state'} \ntest(): {'sender': 'Test AndState', 'state': False} \ntest(): {'sender': 'test()', 'target': 'Test State', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test OrState', 'state': False} \ntest(): {'sender': 'test()', 'target': 'Test StateAlias', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test State', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test State', \n 'command': 'set state', 'new state': False} \ntest(): {'sender': 'Test StateAlias', 'target': 'Test State 2', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test OrState', 'event': 'changed', 'state': True} \ntest(): {'sender': 'Test OrSet', 'target': 'Test State 4', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test State', 'event': 'changed', 'state': False} \ntest(): {'sender': 'Test State 2', 'event': 'changed', 'state': True} \ntest(): {'sender': 'Test State 4', 'event': 'changed', 'state': True} \n\n
\n"}, "controlpi_plugins.state.State": {"fullname": "controlpi_plugins.state.State", "modulename": "controlpi_plugins.state", "qualname": "State", "kind": "class", "doc": "Provide a Boolean state.
\n\nThe state of a State plugin instance can be queried with the \"get state\"\ncommand and set with the \"set state\" command to the new state given by\nthe \"new state\" key:
\n\n\n
>>> import asyncio \n>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test State" : { "plugin" : "State" }}, \n... [{ "target" : "Test State" , "command" : "get state" }, \n... { "target" : "Test State" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State" , "command" : "get state" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', ... \ntest(): {'sender': 'test()', 'target': 'Test State', \n 'command': 'get state'} \ntest(): {'sender': 'test()', 'target': 'Test State', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test State', 'state': False} \ntest(): {'sender': 'test()', 'target': 'Test State', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test State', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test State', \n 'command': 'get state'} \ntest(): {'sender': 'Test State', 'state': True} \ntest(): {'sender': 'Test State', 'state': True} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.state.State.CONF_SCHEMA": {"fullname": "controlpi_plugins.state.State.CONF_SCHEMA", "modulename": "controlpi_plugins.state", "qualname": "State.CONF_SCHEMA", "kind": "variable", "doc": "Schema for State plugin configuration.
\n\nThere are no required or optional configuration keys.
\n", "default_value": "True"}, "controlpi_plugins.state.State.process_conf": {"fullname": "controlpi_plugins.state.State.process_conf", "modulename": "controlpi_plugins.state", "qualname": "State.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.state.State.run": {"fullname": "controlpi_plugins.state.State.run", "modulename": "controlpi_plugins.state", "qualname": "State.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.state.StateAlias": {"fullname": "controlpi_plugins.state.StateAlias", "modulename": "controlpi_plugins.state", "qualname": "StateAlias", "kind": "class", "doc": "Define an alias for another state.
\n\nThe \"alias for\" configuration key gets the name for the other state that\nis aliased by the StateAlias plugin instance.
\n\nThe \"get state\" and \"set state\" commands are forwarded to and the\n\"changed\" events and \"state\" messages are forwarded from this other\nstate:
\n\n\n
>>> import asyncio \n>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test State" : { "plugin" : "State" }, \n... "Test StateAlias" : { "plugin" : "StateAlias" , \n... "alias for" : "Test State" }}, \n... [{ "target" : "Test State" , "command" : "get state" }, \n... { "target" : "Test StateAlias" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test StateAlias" , "command" : "get state" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', ... \ntest(): {'sender': 'test()', 'target': 'Test State', \n 'command': 'get state'} \ntest(): {'sender': 'test()', 'target': 'Test StateAlias', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test State', 'state': False} \ntest(): {'sender': 'test()', 'target': 'Test State', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test StateAlias', 'target': 'Test State', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test StateAlias', 'state': False} \ntest(): {'sender': 'test()', 'target': 'Test StateAlias', \n 'command': 'get state'} \ntest(): {'sender': 'Test State', 'event': 'changed', 'state': True} \ntest(): {'sender': 'Test State', 'state': True} \ntest(): {'sender': 'Test StateAlias', 'target': 'Test State', \n 'command': 'get state'} \ntest(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True} \ntest(): {'sender': 'Test StateAlias', 'state': True} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"fullname": "controlpi_plugins.state.StateAlias.CONF_SCHEMA", "modulename": "controlpi_plugins.state", "qualname": "StateAlias.CONF_SCHEMA", "kind": "variable", "doc": "Schema for StateAlias plugin configuration.
\n\nRequired configuration key:
\n\n\n'alias for': name of aliased state. \n \n", "default_value": "{'properties': {'alias for': {'type': 'string'}}, 'required': ['alias for']}"}, "controlpi_plugins.state.StateAlias.process_conf": {"fullname": "controlpi_plugins.state.StateAlias.process_conf", "modulename": "controlpi_plugins.state", "qualname": "StateAlias.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.state.StateAlias.run": {"fullname": "controlpi_plugins.state.StateAlias.run", "modulename": "controlpi_plugins.state", "qualname": "StateAlias.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.state.AndState": {"fullname": "controlpi_plugins.state.AndState", "modulename": "controlpi_plugins.state", "qualname": "AndState", "kind": "class", "doc": "Define conjunction of states.
\n\nThe \"states\" configuration key gets an array of states to be combined.\nAn AndState plugin client reacts to \"get state\" commands and sends\n\"changed\" events when a change in one of the combined states leads to\na change for the conjunction:
\n\n\n
>>> import asyncio \n>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test State 1" : { "plugin" : "State" }, \n... "Test State 2" : { "plugin" : "State" }, \n... "Test AndState" : { "plugin" : "AndState" , \n... "states" : [ "Test State 1" , "Test State 2" ]}}, \n... [{ "target" : "Test State 1" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State 2" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State 1" , "command" : "set state" , \n... "new state" : False }, \n... { "target" : "Test AndState" , "command" : "get state" }, \n... { "target" : "Test AndState" , "command" : "get sources" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', ... \ntest(): {'sender': 'test()', 'target': 'Test State 1', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'test()', 'target': 'Test State 2', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test State 1', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test State 1', \n 'command': 'set state', 'new state': False} \ntest(): {'sender': 'Test State 2', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test AndState', \n 'command': 'get state'} \ntest(): {'sender': 'Test State 1', 'event': 'changed', 'state': False} \ntest(): {'sender': 'Test AndState', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test AndState', \n 'command': 'get sources'} \ntest(): {'sender': 'Test AndState', 'state': True} \ntest(): {'sender': 'Test AndState', 'event': 'changed', 'state': False} \ntest(): {'sender': 'Test AndState', \n 'states': ['Test State 1', 'Test State 2']} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"fullname": "controlpi_plugins.state.AndState.CONF_SCHEMA", "modulename": "controlpi_plugins.state", "qualname": "AndState.CONF_SCHEMA", "kind": "variable", "doc": "Schema for AndState plugin configuration.
\n\nRequired configuration key:
\n\n\n'states': list of names of combined states. \n \n", "default_value": "{'properties': {'states': {'type': 'array', 'items': {'type': 'string'}}}, 'required': ['states']}"}, "controlpi_plugins.state.AndState.process_conf": {"fullname": "controlpi_plugins.state.AndState.process_conf", "modulename": "controlpi_plugins.state", "qualname": "AndState.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.state.AndState.run": {"fullname": "controlpi_plugins.state.AndState.run", "modulename": "controlpi_plugins.state", "qualname": "AndState.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.state.OrState": {"fullname": "controlpi_plugins.state.OrState", "modulename": "controlpi_plugins.state", "qualname": "OrState", "kind": "class", "doc": "Define disjunction of states.
\n\nThe \"states\" configuration key gets an array of states to be combined.\nAn OrState plugin client reacts to \"get state\" commands and sends\n\"changed\" events when a change in one of the combined states leads to\na change for the disjunction:
\n\n\n
>>> import asyncio \n>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test State 1" : { "plugin" : "State" }, \n... "Test State 2" : { "plugin" : "State" }, \n... "Test OrState" : { "plugin" : "OrState" , \n... "states" : [ "Test State 1" , "Test State 2" ]}}, \n... [{ "target" : "Test State 1" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State 2" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State 1" , "command" : "set state" , \n... "new state" : False }, \n... { "target" : "Test OrState" , "command" : "get state" }, \n... { "target" : "Test OrState" , "command" : "get sources" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', ... \ntest(): {'sender': 'test()', 'target': 'Test State 1', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'test()', 'target': 'Test State 2', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test State 1', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test State 1', \n 'command': 'set state', 'new state': False} \ntest(): {'sender': 'Test State 2', 'event': 'changed', 'state': True} \ntest(): {'sender': 'Test OrState', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test OrState', \n 'command': 'get state'} \ntest(): {'sender': 'Test State 1', 'event': 'changed', 'state': False} \ntest(): {'sender': 'test()', 'target': 'Test OrState', \n 'command': 'get sources'} \ntest(): {'sender': 'Test OrState', 'state': True} \ntest(): {'sender': 'Test OrState', \n 'states': ['Test State 1', 'Test State 2']} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"fullname": "controlpi_plugins.state.OrState.CONF_SCHEMA", "modulename": "controlpi_plugins.state", "qualname": "OrState.CONF_SCHEMA", "kind": "variable", "doc": "Schema for OrState plugin configuration.
\n\nRequired configuration key:
\n\n\n'states': list of names of combined states. \n \n", "default_value": "{'properties': {'states': {'type': 'array', 'items': {'type': 'string'}}}, 'required': ['states']}"}, "controlpi_plugins.state.OrState.process_conf": {"fullname": "controlpi_plugins.state.OrState.process_conf", "modulename": "controlpi_plugins.state", "qualname": "OrState.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.state.OrState.run": {"fullname": "controlpi_plugins.state.OrState.run", "modulename": "controlpi_plugins.state", "qualname": "OrState.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.state.AndSet": {"fullname": "controlpi_plugins.state.AndSet", "modulename": "controlpi_plugins.state", "qualname": "AndSet", "kind": "class", "doc": "Set state based on conjunction of other states.
\n\nThe \"input states\" configuration key gets an array of states used to\ndetermine the state in the \"output state\" configuration key:
\n\n\n
>>> import asyncio \n>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test State 1" : { "plugin" : "State" }, \n... "Test State 2" : { "plugin" : "State" }, \n... "Test State 3" : { "plugin" : "State" }, \n... "Test AndSet" : { "plugin" : "AndSet" , \n... "input states" : [ "Test State 1" , \n... "Test State 2" ], \n... "output state" : "Test State 3" }}, \n... [{ "target" : "Test State 1" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State 2" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test AndSet" , "command" : "get state" }, \n... { "target" : "Test State 1" , "command" : "set state" , \n... "new state" : False }, \n... { "target" : "Test AndSet" , "command" : "get state" }, \n... { "target" : "Test AndSet" , "command" : "get sources" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', ... \ntest(): {'sender': 'test()', 'target': 'Test State 1', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'test()', 'target': 'Test State 2', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test State 1', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test AndSet', \n 'command': 'get state'} \ntest(): {'sender': 'Test State 2', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test State 1', \n 'command': 'set state', 'new state': False} \ntest(): {'sender': 'Test AndSet', 'target': 'Test State 3', \n 'command': 'set state', 'new state': False} \ntest(): {'sender': 'Test AndSet', 'target': 'Test State 3', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'test()', 'target': 'Test AndSet', \n 'command': 'get state'} \ntest(): {'sender': 'Test State 1', 'event': 'changed', 'state': False} \ntest(): {'sender': 'Test State 3', 'state': False} \ntest(): {'sender': 'Test State 3', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test AndSet', \n 'command': 'get sources'} \ntest(): {'sender': 'Test AndSet', 'target': 'Test State 3', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test AndSet', 'target': 'Test State 3', \n 'command': 'set state', 'new state': False} \ntest(): {'sender': 'Test AndSet', \n 'states': ['Test State 1', 'Test State 2']} \ntest(): {'sender': 'Test State 3', 'state': True} \ntest(): {'sender': 'Test State 3', 'event': 'changed', 'state': False} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"fullname": "controlpi_plugins.state.AndSet.CONF_SCHEMA", "modulename": "controlpi_plugins.state", "qualname": "AndSet.CONF_SCHEMA", "kind": "variable", "doc": "Schema for AndSet plugin configuration.
\n\nRequired configuration keys:
\n\n\n'input states': list of names of combined states. \n'output state': name of state to be set. \n \n", "default_value": "{'properties': {'input states': {'type': 'array', 'items': {'type': 'string'}}, 'output state': {'type': 'string'}}, 'required': ['input states', 'output state']}"}, "controlpi_plugins.state.AndSet.process_conf": {"fullname": "controlpi_plugins.state.AndSet.process_conf", "modulename": "controlpi_plugins.state", "qualname": "AndSet.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.state.AndSet.run": {"fullname": "controlpi_plugins.state.AndSet.run", "modulename": "controlpi_plugins.state", "qualname": "AndSet.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.state.OrSet": {"fullname": "controlpi_plugins.state.OrSet", "modulename": "controlpi_plugins.state", "qualname": "OrSet", "kind": "class", "doc": "Set state based on disjunction of other states.
\n\nThe \"input states\" configuration key gets an array of states used to\ndetermine the state in the \"output state\" configuration key:
\n\n\n
>>> import asyncio \n>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test State 1" : { "plugin" : "State" }, \n... "Test State 2" : { "plugin" : "State" }, \n... "Test State 3" : { "plugin" : "State" }, \n... "Test OrSet" : { "plugin" : "OrSet" , \n... "input states" : [ "Test State 1" , \n... "Test State 2" ], \n... "output state" : "Test State 3" }}, \n... [{ "target" : "Test State 1" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test OrSet" , "command" : "get state" }, \n... { "target" : "Test State 2" , "command" : "set state" , \n... "new state" : True }, \n... { "target" : "Test State 1" , "command" : "set state" , \n... "new state" : False }, \n... { "target" : "Test OrSet" , "command" : "get sources" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', ... \ntest(): {'sender': 'test()', 'target': 'Test State 1', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'test()', 'target': 'Test OrSet', \n 'command': 'get state'} \ntest(): {'sender': 'Test State 1', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test State 2', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'Test OrSet', 'target': 'Test State 3', \n 'command': 'set state', 'new state': False} \ntest(): {'sender': 'Test OrSet', 'target': 'Test State 3', \n 'command': 'set state', 'new state': True} \ntest(): {'sender': 'test()', 'target': 'Test State 1', \n 'command': 'set state', 'new state': False} \ntest(): {'sender': 'Test State 2', 'event': 'changed', 'state': True} \ntest(): {'sender': 'Test State 3', 'state': False} \ntest(): {'sender': 'Test State 3', 'event': 'changed', 'state': True} \ntest(): {'sender': 'test()', 'target': 'Test OrSet', \n 'command': 'get sources'} \ntest(): {'sender': 'Test State 1', 'event': 'changed', 'state': False} \ntest(): {'sender': 'Test OrSet', \n 'states': ['Test State 1', 'Test State 2']} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"fullname": "controlpi_plugins.state.OrSet.CONF_SCHEMA", "modulename": "controlpi_plugins.state", "qualname": "OrSet.CONF_SCHEMA", "kind": "variable", "doc": "Schema for OrSet plugin configuration.
\n\nRequired configuration keys:
\n\n\n'input states': list of names of combined states. \n'output state': name of state to be set. \n \n", "default_value": "{'properties': {'input states': {'type': 'array', 'items': {'type': 'string'}}, 'output state': {'type': 'string'}}, 'required': ['input states', 'output state']}"}, "controlpi_plugins.state.OrSet.process_conf": {"fullname": "controlpi_plugins.state.OrSet.process_conf", "modulename": "controlpi_plugins.state", "qualname": "OrSet.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.state.OrSet.run": {"fullname": "controlpi_plugins.state.OrSet.run", "modulename": "controlpi_plugins.state", "qualname": "OrSet.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.util": {"fullname": "controlpi_plugins.util", "modulename": "controlpi_plugins.util", "kind": "module", "doc": "Provide utility plugins for all kinds of systems.
\n\n\nLog logs messages on stdout. \nInit sends list of messages on startup and on demand. \nExecute sends configurable list of messages on demand. \nAlias translates messages to an alias. \n \n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test Log" : { "plugin" : "Log" , \n... "filter" : [{ "sender" : { "const" : "Test Alias" }}]}, \n... "Test Init" : { "plugin" : "Init" , \n... "messages" : [{ "id" : 42 , "content" : "Test Message" }]}, \n... "Test Alias" : { "plugin" : "Alias" , \n... "from" : { "sender" : { "const" : "Test Init" }, \n... "id" : { "const" : 42 }}, \n... "to" : { "id" : "translated" }}}, [])) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Log', 'plugin': 'Log', \n 'sends': [], 'receives': [{'sender': {'const': 'Test Alias'}}]} \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Init', 'plugin': 'Init', \n 'sends': [{'id': {'const': 42}, \n 'content': {'const': 'Test Message'}}], \n 'receives': [{'target': {'const': 'Test Init'}, \n 'command': {'const': 'execute'}}]} \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Alias', 'plugin': 'Alias', \n 'sends': [{'id': {'const': 'translated'}}], \n 'receives': [{'sender': {'const': 'Test Init'}, \n 'id': {'const': 42}}]} \ntest(): {'sender': 'Test Init', 'id': 42, \n 'content': 'Test Message'} \ntest(): {'sender': 'Test Alias', 'id': 'translated', \n 'content': 'Test Message'} \nTest Log: {'sender': 'Test Alias', 'id': 'translated', \n 'content': 'Test Message'} \n\n
\n"}, "controlpi_plugins.util.Log": {"fullname": "controlpi_plugins.util.Log", "modulename": "controlpi_plugins.util", "qualname": "Log", "kind": "class", "doc": "Log messages on stdout.
\n\nThe \"filter\" configuration key gets a list of message templates defining\nthe messages that should be logged by the plugin instance.
\n\nIn the following example the first and third message match the given\ntemplate and are logged by the instance \"Test Log\", while the second\nmessage does not match and is only logged by the test, but not by the\nLog instance:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test Log" : { "plugin" : "Log" , \n... "filter" : [{ "id" : { "const" : 42 }}]}}, \n... [{ "id" : 42 , "message" : "Test Message" }, \n... { "id" : 42.42 , "message" : "Second Message" }, \n... { "id" : 42 , "message" : "Third Message" }])) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Log', 'plugin': 'Log', \n 'sends': [], 'receives': [{'id': {'const': 42}}]} \ntest(): {'sender': 'test()', 'id': 42, 'message': 'Test Message'} \nTest Log: {'sender': 'test()', 'id': 42, 'message': 'Test Message'} \ntest(): {'sender': 'test()', 'id': 42.42, 'message': 'Second Message'} \ntest(): {'sender': 'test()', 'id': 42, 'message': 'Third Message'} \nTest Log: {'sender': 'test()', 'id': 42, 'message': 'Third Message'} \n\n
\n\nThe \"filter\" key is required:
\n\n\n
>>> asyncio . run ( controlpi . test ( \n... { "Test Log" : { "plugin" : "Log" }}, [])) \ndata must contain ['filter'] properties \nConfiguration for 'Test Log' is not valid. \n\n
\n\nThe \"filter\" key has to contain a list of message templates, i.e.,\nJSON objects:
\n\n\n
>>> asyncio . run ( controlpi . test ( \n... { "Test Log" : { "plugin" : "Log" , \n... "filter" : [ 42 ]}}, [])) \ndata.filter[0] must be object \nConfiguration for 'Test Log' is not valid. \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"fullname": "controlpi_plugins.util.Log.CONF_SCHEMA", "modulename": "controlpi_plugins.util", "qualname": "Log.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Log plugin configuration.
\n\nRequired configuration key:
\n\n\n'filter': list of message templates to be logged. \n \n", "default_value": "{'properties': {'filter': {'type': 'array', 'items': {'type': 'object'}}}, 'required': ['filter']}"}, "controlpi_plugins.util.Log.process_conf": {"fullname": "controlpi_plugins.util.Log.process_conf", "modulename": "controlpi_plugins.util", "qualname": "Log.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.util.Log.run": {"fullname": "controlpi_plugins.util.Log.run", "modulename": "controlpi_plugins.util", "qualname": "Log.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.util.Init": {"fullname": "controlpi_plugins.util.Init", "modulename": "controlpi_plugins.util", "qualname": "Init", "kind": "class", "doc": "Send list of messages on startup and on demand.
\n\nThe \"messages\" configuration key gets a list of messages to be sent on\nstartup. The same list is sent in reaction to a message with\n\"target\": NAME and \"command\": \"execute\".
\n\nIn the example, the two configured messages are sent twice, once at\nstartup and a second time in reaction to the \"execute\" command sent by\nthe test:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test Init" : { "plugin" : "Init" , \n... "messages" : [{ "id" : 42 , \n... "content" : "Test Message" }, \n... { "id" : 42.42 , \n... "content" : "Second Message" }]}}, \n... [{ "target" : "Test Init" , "command" : "execute" }])) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Init', 'plugin': 'Init', \n 'sends': [{'id': {'const': 42}, \n 'content': {'const': 'Test Message'}}, \n {'id': {'const': 42.42}, \n 'content': {'const': 'Second Message'}}], \n 'receives': [{'target': {'const': 'Test Init'}, \n 'command': {'const': 'execute'}}]} \ntest(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'} \ntest(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'} \ntest(): {'sender': 'test()', 'target': 'Test Init', 'command': 'execute'} \ntest(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'} \ntest(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'} \n\n
\n\nThe \"messages\" key is required:
\n\n\n
>>> asyncio . run ( controlpi . test ( \n... { "Test Init" : { "plugin" : "Init" }}, [])) \ndata must contain ['messages'] properties \nConfiguration for 'Test Init' is not valid. \n\n
\n\nThe \"messages\" key has to contain a list of (partial) messages, i.e.,\nJSON objects:
\n\n\n
>>> asyncio . run ( controlpi . test ( \n... { "Test Init" : { "plugin" : "Init" , \n... "messages" : [ 42 ]}}, [])) \ndata.messages[0] must be object \nConfiguration for 'Test Init' is not valid. \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"fullname": "controlpi_plugins.util.Init.CONF_SCHEMA", "modulename": "controlpi_plugins.util", "qualname": "Init.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Init plugin configuration.
\n\nRequired configuration key:
\n\n\n'messages': list of messages to be sent. \n \n", "default_value": "{'properties': {'messages': {'type': 'array', 'items': {'type': 'object'}}}, 'required': ['messages']}"}, "controlpi_plugins.util.Init.process_conf": {"fullname": "controlpi_plugins.util.Init.process_conf", "modulename": "controlpi_plugins.util", "qualname": "Init.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.util.Init.run": {"fullname": "controlpi_plugins.util.Init.run", "modulename": "controlpi_plugins.util", "qualname": "Init.run", "kind": "function", "doc": "Send configured messages on startup.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.util.Execute": {"fullname": "controlpi_plugins.util.Execute", "modulename": "controlpi_plugins.util", "qualname": "Execute", "kind": "class", "doc": "Send configurable list of messages on demand.
\n\nAn Execute plugin instance receives two kinds of commands.\nThe \"set messages\" command has a \"messages\" key with a list of (partial)\nmessages, which are sent by the Execute instance in reaction to an\n\"execute\" command.
\n\nIn the example, the first command sent by the test sets two messages,\nwhich are then sent in reaction to the second command sent by the test:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test Execute" : { "plugin" : "Execute" }}, \n... [{ "target" : "Test Execute" , "command" : "set messages" , \n... "messages" : [{ "id" : 42 , "content" : "Test Message" }, \n... { "id" : 42.42 , "content" : "Second Message" }]}, \n... { "target" : "Test Execute" , "command" : "execute" }])) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Execute', 'plugin': 'Execute', \n 'sends': [{}], \n 'receives': [{'target': {'const': 'Test Execute'}, \n 'command': {'const': 'set messages'}, \n 'messages': {'type': 'array', \n 'items': {'type': 'object'}}}, \n {'target': {'const': 'Test Execute'}, \n 'command': {'const': 'execute'}}]} \ntest(): {'sender': 'test()', 'target': 'Test Execute', \n 'command': 'set messages', \n 'messages': [{'id': 42, 'content': 'Test Message'}, \n {'id': 42.42, 'content': 'Second Message'}]} \ntest(): {'sender': 'test()', 'target': 'Test Execute', \n 'command': 'execute'} \ntest(): {'sender': 'Test Execute', 'id': 42, \n 'content': 'Test Message'} \ntest(): {'sender': 'Test Execute', 'id': 42.42, \n 'content': 'Second Message'} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"fullname": "controlpi_plugins.util.Execute.CONF_SCHEMA", "modulename": "controlpi_plugins.util", "qualname": "Execute.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Execute plugin configuration.
\n\nThere are no required or optional configuration keys.
\n", "default_value": "True"}, "controlpi_plugins.util.Execute.process_conf": {"fullname": "controlpi_plugins.util.Execute.process_conf", "modulename": "controlpi_plugins.util", "qualname": "Execute.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.util.Execute.run": {"fullname": "controlpi_plugins.util.Execute.run", "modulename": "controlpi_plugins.util", "qualname": "Execute.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.util.Alias": {"fullname": "controlpi_plugins.util.Alias", "modulename": "controlpi_plugins.util", "qualname": "Alias", "kind": "class", "doc": "Translate messages to an alias.
\n\nThe \"from\" configuration key gets a message template and the\nconfiguration key \"to\" a (partial) message. The \"translate\"\nconfiguration key contains pairs of message keys, where the \"from\"\nmessage key is translated to the \"to\" message key if present in the\nmessage.
\n\nAll messages matching the \"from\" template are received by the Alias\ninstance and a message translated by adding the keys and values of the\n\"to\" message and the translated key-value pairs according to\n\"translate\" is sent. Keys that are not \"sender\" and not modified by\n\"to\" or \"translate\" are retained.
\n\nIn the example, the two messages sent by the test are translated by the\nAlias instance and the translated messages are sent by it preserving\nthe \"content\" keys:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test Alias" : { "plugin" : "Alias" , \n... "from" : { "id" : { "const" : 42 }}, \n... "to" : { "id" : "translated" }, \n... "translate" : [{ 'from' : "old" , "to" : "new" }]}}, \n... [{ "id" : 42 , "content" : "Test Message" , "old" : "content" }, \n... { "id" : 42 , "content" : "Second Message" , "old" : "content" }])) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Alias', 'plugin': 'Alias', \n 'sends': [{'id': {'const': 'translated'}}], \n 'receives': [{'id': {'const': 42}}]} \ntest(): {'sender': 'test()', 'id': 42, \n 'content': 'Test Message', 'old': 'content'} \ntest(): {'sender': 'test()', 'id': 42, \n 'content': 'Second Message', 'old': 'content'} \ntest(): {'sender': 'Test Alias', 'id': 'translated', \n 'content': 'Test Message', 'old': 'content', 'new': 'content'} \ntest(): {'sender': 'Test Alias', 'id': 'translated', \n 'content': 'Second Message', 'old': 'content', 'new': 'content'} \n\n
\n\nAn Alias instance can also translate to a list of messages instead of\na single message:
\n\n\n
>>> asyncio . run ( controlpi . test ( \n... { "Test Alias" : { "plugin" : "Alias" , \n... "from" : { "id" : { "const" : 42 }}, \n... "to" : [{ "id" : "first" }, { "id" : "second" }], \n... "translate" : [{ 'from' : "old" , "to" : "new" }]}}, \n... [{ "id" : 42 , "content" : "Test Message" , "old" : "content" }])) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Alias', 'plugin': 'Alias', \n 'sends': [{'id': {'const': 'first'}}, \n {'id': {'const': 'second'}}], \n 'receives': [{'id': {'const': 42}}]} \ntest(): {'sender': 'test()', 'id': 42, \n 'content': 'Test Message', 'old': 'content'} \ntest(): {'sender': 'Test Alias', 'id': 'first', \n 'content': 'Test Message', 'old': 'content', 'new': 'content'} \ntest(): {'sender': 'Test Alias', 'id': 'second', \n 'content': 'Test Message', 'old': 'content', 'new': 'content'} \n\n
\n\nThe \"from\" key is required:
\n\n\n
>>> asyncio . run ( controlpi . test ( \n... { "Test Alias" : { "plugin" : "Alias" }}, [])) \ndata must contain ['from'] properties \nConfiguration for 'Test Alias' is not valid. \n\n
\n\nThe \"from\" key has to contain a message template and the \"to\" key a\n(partial) message, i.e., both have to be JSON objects:
\n\n\n
>>> asyncio . run ( controlpi . test ( \n... { "Test Alias" : { "plugin" : "Alias" , \n... "from" : 42 , \n... "to" : 42 }}, [])) \ndata.from must be object \nConfiguration for 'Test Alias' is not valid. \n>>> asyncio . run ( controlpi . test ( \n... { "Test Alias" : { "plugin" : "Alias" , \n... "from" : { "id" : { "const" : 42 }}, \n... "to" : 42 }}, [])) \ndata.to cannot be validated by any definition \nConfiguration for 'Test Alias' is not valid. \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"fullname": "controlpi_plugins.util.Alias.CONF_SCHEMA", "modulename": "controlpi_plugins.util", "qualname": "Alias.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Alias plugin configuration.
\n\nRequired configuration keys:
\n\n\n'from': template of messages to be translated. \n \n\nOptional configuration keys:
\n\n\n'to': translated message(s) to be sent. \n'translate': array of pairs of keys to be translated. \n \n", "default_value": "{'properties': {'from': {'type': 'object'}, 'to': {'anyOf': [{'type': 'object'}, {'type': 'array', 'items': {'type': 'object'}}]}, 'translate': {'type': 'array', 'items': {'type': 'object', 'properties': {'from': {'type': 'string'}, 'to': {'type': 'string'}}}}}, 'required': ['from']}"}, "controlpi_plugins.util.Alias.process_conf": {"fullname": "controlpi_plugins.util.Alias.process_conf", "modulename": "controlpi_plugins.util", "qualname": "Alias.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.util.Alias.run": {"fullname": "controlpi_plugins.util.Alias.run", "modulename": "controlpi_plugins.util", "qualname": "Alias.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.util.Counter": {"fullname": "controlpi_plugins.util.Counter", "modulename": "controlpi_plugins.util", "qualname": "Counter", "kind": "class", "doc": "Count messages confirming to a given template.
\n\nThe plugin counts messages confirming to the given template. The\ncounter can be queried and reset by commands. The 'reset' command also\nqueries the last count before the reset:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test Counter" : { "plugin" : "Counter" , \n... "count" : { "id" : { "const" : 42 }}}}, \n... [{ "target" : "Test Counter" , "command" : "get count" }, \n... { "id" : 42 }, { "id" : 42 }, { "id" : 49 }, \n... { "target" : "Test Counter" , "command" : "get count" }, \n... { "id" : 42 }, { "id" : 42 }, { "id" : 42 }, \n... { "target" : "Test Counter" , "command" : "reset" }, \n... { "target" : "Test Counter" , "command" : "get count" }, \n... { "id" : 42 }, { "id" : 42 }, { "id" : 42 }, \n... { "target" : "Test Counter" , "command" : "get count" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Counter', 'plugin': 'Counter', \n 'sends': [{'count': {'type': 'integer'}}], \n 'receives': [{'id': {'const': 42}}, \n {'target': {'const': 'Test Counter'}, \n 'command': {'const': 'get count'}}, \n {'target': {'const': 'Test Counter'}, \n 'command': {'const': 'reset'}}]} \ntest(): {'sender': 'test()', 'target': 'Test Counter', \n 'command': 'get count'} \ntest(): {'sender': 'test()', 'id': 42} \ntest(): {'sender': 'Test Counter', 'count': 0, \n 'since': ..., 'until': ...} \ntest(): {'sender': 'test()', 'id': 42} \ntest(): {'sender': 'test()', 'id': 49} \ntest(): {'sender': 'test()', 'target': 'Test Counter', \n 'command': 'get count'} \ntest(): {'sender': 'test()', 'id': 42} \ntest(): {'sender': 'Test Counter', 'count': 2, \n 'since': ..., 'until': ...} \ntest(): {'sender': 'test()', 'id': 42} \ntest(): {'sender': 'test()', 'id': 42} \ntest(): {'sender': 'test()', 'target': 'Test Counter', \n 'command': 'reset'} \ntest(): {'sender': 'test()', 'target': 'Test Counter', \n 'command': 'get count'} \ntest(): {'sender': 'Test Counter', 'count': 5, \n 'since': ..., 'until': ...} \ntest(): {'sender': 'test()', 'id': 42} \ntest(): {'sender': 'Test Counter', 'count': 0, \n 'since': ..., 'until': ...} \ntest(): {'sender': 'test()', 'id': 42} \ntest(): {'sender': 'test()', 'id': 42} \ntest(): {'sender': 'test()', 'target': 'Test Counter', \n 'command': 'get count'} \ntest(): {'sender': 'Test Counter', 'count': 3, \n 'since': ..., 'until': ...} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"fullname": "controlpi_plugins.util.Counter.CONF_SCHEMA", "modulename": "controlpi_plugins.util", "qualname": "Counter.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Counter plugin configuration.
\n\nRequired configuration key:
\n\n\n'count': template of messages to be counted. \n \n", "default_value": "{'properties': {'count': {'type': 'object'}, 'date format': {'type': 'string', 'default': '%Y-%m-%d %H:%M:%S'}}, 'required': ['count']}"}, "controlpi_plugins.util.Counter.process_conf": {"fullname": "controlpi_plugins.util.Counter.process_conf", "modulename": "controlpi_plugins.util", "qualname": "Counter.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.util.Counter.run": {"fullname": "controlpi_plugins.util.Counter.run", "modulename": "controlpi_plugins.util", "qualname": "Counter.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.util.Date": {"fullname": "controlpi_plugins.util.Date", "modulename": "controlpi_plugins.util", "qualname": "Date", "kind": "class", "doc": "Send message with current date.
\n\nThe plugin reacts to 'get date' commands by sending messages with\na 'date' key:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test Date" : { "plugin" : "Date" }}, \n... [{ "target" : "Test Date" , "command" : "get date" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Date', 'plugin': 'Date', \n 'sends': [{'date': {'type': 'string'}}], \n 'receives': [{'target': {'const': 'Test Date'}, \n 'command': {'const': 'get date'}}]} \ntest(): {'sender': 'test()', 'target': 'Test Date', \n 'command': 'get date'} \ntest(): {'sender': 'Test Date', 'date': ...} \n\n
\n\nThe format of the date can be configured with the 'format'\nconfiguration key:
\n\n\n
>>> asyncio . run ( controlpi . test ( \n... { "Test Date" : { "plugin" : "Date" , \n... "format" : "%Y%m %d %H%M%S %f " }}, \n... [{ "target" : "Test Date" , "command" : "get date" }])) \n... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Date', 'plugin': 'Date', \n 'sends': [{'date': {'type': 'string'}}], \n 'receives': [{'target': {'const': 'Test Date'}, \n 'command': {'const': 'get date'}}]} \ntest(): {'sender': 'test()', 'target': 'Test Date', \n 'command': 'get date'} \ntest(): {'sender': 'Test Date', 'date': ...} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"fullname": "controlpi_plugins.util.Date.CONF_SCHEMA", "modulename": "controlpi_plugins.util", "qualname": "Date.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Date plugin configuration.
\n\nOptional configuration key:
\n\n\n'format': format for the sent datetime string.\nDefault: '%Y-%m-%d %H:%M:%S' \n \n", "default_value": "{'properties': {'format': {'type': 'string', 'default': '%Y-%m-%d %H:%M:%S'}}}"}, "controlpi_plugins.util.Date.process_conf": {"fullname": "controlpi_plugins.util.Date.process_conf", "modulename": "controlpi_plugins.util", "qualname": "Date.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.util.Date.run": {"fullname": "controlpi_plugins.util.Date.run", "modulename": "controlpi_plugins.util", "qualname": "Date.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.wait": {"fullname": "controlpi_plugins.wait", "modulename": "controlpi_plugins.wait", "kind": "module", "doc": "Provide waiting/sleeping plugins for all kinds of systems.
\n\n\nWait waits for time defined in configuration and sends \"finished\" event. \nGenericWait waits for time defined in \"wait\" command and sends \"finished\"\nevent with \"id\" string defined in \"wait\" command. \n \n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test Wait" : { "plugin" : "Wait" , "seconds" : 0.01 }, \n... "Test GenericWait" : { "plugin" : "GenericWait" }}, \n... [{ "target" : "Test GenericWait" , "command" : "wait" , \n... "seconds" : 0.02 , "id" : "Long Wait" }, \n... { "target" : "Test Wait" , "command" : "wait" }], 0.025 )) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test Wait', 'plugin': 'Wait', \n 'sends': [{'event': {'const': 'finished'}}], \n 'receives': [{'target': {'const': 'Test Wait'}, \n 'command': {'const': 'wait'}}]} \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test GenericWait', 'plugin': 'GenericWait', \n 'sends': [{'event': {'const': 'finished'}, \n 'id': {'type': 'string'}}], \n 'receives': [{'target': {'const': 'Test GenericWait'}, \n 'command': {'const': 'wait'}, \n 'seconds': {'type': 'number'}, \n 'id': {'type': 'string'}}]} \ntest(): {'sender': 'test()', 'target': 'Test GenericWait', \n 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'} \ntest(): {'sender': 'test()', 'target': 'Test Wait', 'command': 'wait'} \ntest(): {'sender': 'Test Wait', 'event': 'finished'} \ntest(): {'sender': 'Test GenericWait', 'event': 'finished', \n 'id': 'Long Wait'} \n\n
\n"}, "controlpi_plugins.wait.Wait": {"fullname": "controlpi_plugins.wait.Wait", "modulename": "controlpi_plugins.wait", "qualname": "Wait", "kind": "class", "doc": "Wait for time defined in configuration.
\n\nThe \"seconds\" configuration key gets the number of seconds to wait after\nreceiving a \"wait\" command before sending the \"finished\" event:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Long Wait" : { "plugin" : "Wait" , "seconds" : 0.02 }, \n... "Short Wait" : { "plugin" : "Wait" , "seconds" : 0.01 }}, \n... [{ "target" : "Long Wait" , "command" : "wait" }, \n... { "target" : "Short Wait" , "command" : "wait" }], 0.025 )) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Long Wait', 'plugin': 'Wait', \n 'sends': [{'event': {'const': 'finished'}}], \n 'receives': [{'target': {'const': 'Long Wait'}, \n 'command': {'const': 'wait'}}]} \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Short Wait', 'plugin': 'Wait', \n 'sends': [{'event': {'const': 'finished'}}], \n 'receives': [{'target': {'const': 'Short Wait'}, \n 'command': {'const': 'wait'}}]} \ntest(): {'sender': 'test()', 'target': 'Long Wait', 'command': 'wait'} \ntest(): {'sender': 'test()', 'target': 'Short Wait', 'command': 'wait'} \ntest(): {'sender': 'Short Wait', 'event': 'finished'} \ntest(): {'sender': 'Long Wait', 'event': 'finished'} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"fullname": "controlpi_plugins.wait.Wait.CONF_SCHEMA", "modulename": "controlpi_plugins.wait", "qualname": "Wait.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Wait plugin configuration.
\n\nRequired configuration key:
\n\n\n'seconds': number of seconds to wait. \n \n", "default_value": "{'properties': {'seconds': {'type': 'number'}}, 'required': ['seconds']}"}, "controlpi_plugins.wait.Wait.process_conf": {"fullname": "controlpi_plugins.wait.Wait.process_conf", "modulename": "controlpi_plugins.wait", "qualname": "Wait.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.wait.Wait.run": {"fullname": "controlpi_plugins.wait.Wait.run", "modulename": "controlpi_plugins.wait", "qualname": "Wait.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.wait.GenericWait": {"fullname": "controlpi_plugins.wait.GenericWait", "modulename": "controlpi_plugins.wait", "qualname": "GenericWait", "kind": "class", "doc": "Wait for time defined in \"wait\" command.
\n\nThe \"wait\" command has message keys \"seconds\" defining the seconds to\nwait and \"id\" defining a string to be sent back in the \"finished\" event\nafter the wait:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Test GenericWait" : { "plugin" : "GenericWait" }}, \n... [{ "target" : "Test GenericWait" , "command" : "wait" , \n... "seconds" : 0.02 , "id" : "Long Wait" }, \n... { "target" : "Test GenericWait" , "command" : "wait" , \n... "seconds" : 0.01 , "id" : "Short Wait" }], 0.025 )) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Test GenericWait', 'plugin': 'GenericWait', \n 'sends': [{'event': {'const': 'finished'}, \n 'id': {'type': 'string'}}], \n 'receives': [{'target': {'const': 'Test GenericWait'}, \n 'command': {'const': 'wait'}, \n 'seconds': {'type': 'number'}, \n 'id': {'type': 'string'}}]} \ntest(): {'sender': 'test()', 'target': 'Test GenericWait', \n 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'} \ntest(): {'sender': 'test()', 'target': 'Test GenericWait', \n 'command': 'wait', 'seconds': 0.01, 'id': 'Short Wait'} \ntest(): {'sender': 'Test GenericWait', 'event': 'finished', \n 'id': 'Short Wait'} \ntest(): {'sender': 'Test GenericWait', 'event': 'finished', \n 'id': 'Long Wait'} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"fullname": "controlpi_plugins.wait.GenericWait.CONF_SCHEMA", "modulename": "controlpi_plugins.wait", "qualname": "GenericWait.CONF_SCHEMA", "kind": "variable", "doc": "Schema for GenericWait plugin configuration.
\n\nThere are no required or optional configuration keys.
\n", "default_value": "True"}, "controlpi_plugins.wait.GenericWait.process_conf": {"fullname": "controlpi_plugins.wait.GenericWait.process_conf", "modulename": "controlpi_plugins.wait", "qualname": "GenericWait.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.wait.GenericWait.run": {"fullname": "controlpi_plugins.wait.GenericWait.run", "modulename": "controlpi_plugins.wait", "qualname": "GenericWait.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.wait.Timer": {"fullname": "controlpi_plugins.wait.Timer", "modulename": "controlpi_plugins.wait", "qualname": "Timer", "kind": "class", "doc": "Timer that can be started and cancelled.
\n\nThe \"seconds\" configuration key gets the number of seconds to wait after\nreceiving a \"start\" command before sending the \"finished\" event.\nThe \"cancel\" command cancels all outstanding \"finished\" events and sends\na corresponding \"cancelled\" event:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Timer" : { "plugin" : "Timer" , "seconds" : 0.01 }}, \n... [{ "target" : "Timer" , "command" : "start" }, \n... { "target" : "Timer" , "command" : "start" }, \n... { "target" : "Timer" , "command" : "cancel" }, \n... { "target" : "Timer" , "command" : "start" }, \n... { "target" : "Timer" , "command" : "start" }], 0.015 )) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Timer', 'plugin': 'Timer', \n 'sends': [{'event': {'const': 'finished'}}, \n {'event': {'const': 'cancelled'}}], \n 'receives': [{'target': {'const': 'Timer'}, \n 'command': {'const': 'start'}}, \n {'target': {'const': 'Timer'}, \n 'command': {'const': 'cancel'}}]} \ntest(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'} \ntest(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'} \ntest(): {'sender': 'test()', 'target': 'Timer', 'command': 'cancel'} \ntest(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'} \ntest(): {'sender': 'Timer', 'event': 'cancelled', 'count': 2} \ntest(): {'sender': 'test()', 'target': 'Timer', 'command': 'start'} \ntest(): {'sender': 'Timer', 'event': 'finished'} \ntest(): {'sender': 'Timer', 'event': 'finished'} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"fullname": "controlpi_plugins.wait.Timer.CONF_SCHEMA", "modulename": "controlpi_plugins.wait", "qualname": "Timer.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Timer plugin configuration.
\n\nRequired configuration key:
\n\n\n'seconds': number of seconds to wait. \n \n", "default_value": "{'properties': {'seconds': {'type': 'number'}}, 'required': ['seconds']}"}, "controlpi_plugins.wait.Timer.process_conf": {"fullname": "controlpi_plugins.wait.Timer.process_conf", "modulename": "controlpi_plugins.wait", "qualname": "Timer.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.wait.Timer.run": {"fullname": "controlpi_plugins.wait.Timer.run", "modulename": "controlpi_plugins.wait", "qualname": "Timer.run", "kind": "function", "doc": "Run no code proactively.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}, "controlpi_plugins.wait.Periodic": {"fullname": "controlpi_plugins.wait.Periodic", "modulename": "controlpi_plugins.wait", "qualname": "Periodic", "kind": "class", "doc": "Send message periodically.
\n\nThe \"seconds\" configuration key is the period of the repetition:\nreceiving a \"wait\" command before sending the \"finished\" event:
\n\n\n
>>> import controlpi \n>>> asyncio . run ( controlpi . test ( \n... { "Loop" : { "plugin" : "Periodic" , "seconds" : 0.01 , \n... "message" : { "key" : "value" }}}, \n... [], 0.025 )) \n... # doctest: +NORMALIZE_WHITESPACE \ntest(): {'sender': '', 'event': 'registered', \n 'client': 'Loop', 'plugin': 'Periodic', \n 'sends': [{'key': {'const': 'value'}}], 'receives': []} \ntest(): {'sender': 'Loop', 'key': 'value'} \ntest(): {'sender': 'Loop', 'key': 'value'} \n\n
\n", "bases": "controlpi.baseplugin.BasePlugin"}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"fullname": "controlpi_plugins.wait.Periodic.CONF_SCHEMA", "modulename": "controlpi_plugins.wait", "qualname": "Periodic.CONF_SCHEMA", "kind": "variable", "doc": "Schema for Wait plugin configuration.
\n\nRequired configuration key:
\n\n\n'seconds': period of repetition in seconds. \n'message': message to send periodically. \n \n", "default_value": "{'properties': {'seconds': {'type': 'number'}, 'message': {'type': 'object'}}, 'required': ['seconds', 'message']}"}, "controlpi_plugins.wait.Periodic.process_conf": {"fullname": "controlpi_plugins.wait.Periodic.process_conf", "modulename": "controlpi_plugins.wait", "qualname": "Periodic.process_conf", "kind": "function", "doc": "Register plugin as bus client.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "controlpi_plugins.wait.Periodic.run": {"fullname": "controlpi_plugins.wait.Periodic.run", "modulename": "controlpi_plugins.wait", "qualname": "Periodic.run", "kind": "function", "doc": "Run periodic loop.
\n", "signature": "(self ) -> None : ", "funcdef": "async def"}}, "docInfo": {"controlpi": {"qualname": 0, "fullname": 1, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 82}, "controlpi.CONF_SCHEMA": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 26, "signature": 0, "bases": 0, "doc": 3}, "controlpi.run": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 41, "bases": 0, "doc": 536}, "controlpi.test": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 88, "bases": 0, "doc": 750}, "controlpi.baseplugin": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 1755}, "controlpi.baseplugin.JSONSchema": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 22, "signature": 0, "bases": 0, "doc": 3}, "controlpi.baseplugin.PluginConf": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "controlpi.baseplugin.ConfException": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 9}, "controlpi.baseplugin.BasePlugin": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 1076}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"qualname": 3, "fullname": 5, "annotation": 18, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "controlpi.baseplugin.BasePlugin.bus": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "controlpi.baseplugin.BasePlugin.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "controlpi.baseplugin.BasePlugin.conf": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "controlpi.baseplugin.BasePlugin.process_conf": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 50}, "controlpi.baseplugin.BasePlugin.run": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 49}, "controlpi.messagebus": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 1501}, "controlpi.messagebus.MessageValue": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 18, "signature": 0, "bases": 0, "doc": 3}, "controlpi.messagebus.JSONSchema": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 22, "signature": 0, "bases": 0, "doc": 3}, "controlpi.messagebus.MessageCallback": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 12, "signature": 0, "bases": 0, "doc": 3}, "controlpi.messagebus.register_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 90, "bases": 0, "doc": 12}, "controlpi.messagebus.validate": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 86, "bases": 0, "doc": 13}, "controlpi.messagebus.Message": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 20, "doc": 725}, "controlpi.messagebus.Message.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 103, "bases": 0, "doc": 178}, "controlpi.messagebus.Message.check_value": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 74, "bases": 0, "doc": 670}, "controlpi.messagebus.Message.update": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 28, "bases": 0, "doc": 556}, "controlpi.messagebus.Message.setdefault": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 152, "bases": 0, "doc": 340}, "controlpi.messagebus.MessageTemplate": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 24, "doc": 494}, "controlpi.messagebus.MessageTemplate.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 108, "bases": 0, "doc": 148}, "controlpi.messagebus.MessageTemplate.from_message": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 40, "bases": 0, "doc": 473}, "controlpi.messagebus.MessageTemplate.update": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 28, "bases": 0, "doc": 1029}, "controlpi.messagebus.MessageTemplate.setdefault": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 190, "bases": 0, "doc": 556}, "controlpi.messagebus.MessageTemplate.check": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 34, "bases": 0, "doc": 1682}, "controlpi.messagebus.TemplateRegistry": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3554}, "controlpi.messagebus.TemplateRegistry.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 4, "bases": 0, "doc": 37}, "controlpi.messagebus.TemplateRegistry.insert": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 109, "bases": 0, "doc": 416}, "controlpi.messagebus.TemplateRegistry.delete": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 476}, "controlpi.messagebus.TemplateRegistry.check": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 44, "bases": 0, "doc": 766}, "controlpi.messagebus.TemplateRegistry.get": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 40, "bases": 0, "doc": 466}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 78, "bases": 0, "doc": 12}, "controlpi.messagebus.TemplateRegistry.get_templates": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 40, "bases": 0, "doc": 762}, "controlpi.messagebus.BusException": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 10}, "controlpi.messagebus.MessageBus": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 1839}, "controlpi.messagebus.MessageBus.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 4, "bases": 0, "doc": 79}, "controlpi.messagebus.MessageBus.register": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 141, "bases": 0, "doc": 488}, "controlpi.messagebus.MessageBus.unregister": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 282}, "controlpi.messagebus.MessageBus.run": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 177}, "controlpi.messagebus.MessageBus.send": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 34, "bases": 0, "doc": 852}, "controlpi.messagebus.MessageBus.send_nowait": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 34, "bases": 0, "doc": 848}, "controlpi.pluginregistry": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 465}, "controlpi.pluginregistry.PluginRegistry": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 376}, "controlpi.pluginregistry.PluginRegistry.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 26, "bases": 0, "doc": 261}, "controlpi_plugins": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "controlpi_plugins.state": {"qualname": 0, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 1650}, "controlpi_plugins.state.State": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 618}, "controlpi_plugins.state.State.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 19}, "controlpi_plugins.state.State.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.state.State.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.state.StateAlias": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 810}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 30, "signature": 0, "bases": 0, "doc": 28}, "controlpi_plugins.state.StateAlias.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.state.StateAlias.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.state.AndState": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 948}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 40, "signature": 0, "bases": 0, "doc": 29}, "controlpi_plugins.state.AndState.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.state.AndState.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.state.OrState": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 923}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 40, "signature": 0, "bases": 0, "doc": 29}, "controlpi_plugins.state.OrState.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.state.OrState.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.state.AndSet": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 1260}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 60, "signature": 0, "bases": 0, "doc": 43}, "controlpi_plugins.state.AndSet.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.state.AndSet.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.state.OrSet": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 1056}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 60, "signature": 0, "bases": 0, "doc": 43}, "controlpi_plugins.state.OrSet.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.state.OrSet.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.util": {"qualname": 0, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 806}, "controlpi_plugins.util.Log": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 774}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 40, "signature": 0, "bases": 0, "doc": 30}, "controlpi_plugins.util.Log.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.util.Log.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.util.Init": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 850}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 40, "signature": 0, "bases": 0, "doc": 29}, "controlpi_plugins.util.Init.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.util.Init.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.util.Execute": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 685}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 19}, "controlpi_plugins.util.Execute.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.util.Execute.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.util.Alias": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 1688}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 122, "signature": 0, "bases": 0, "doc": 64}, "controlpi_plugins.util.Alias.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.util.Alias.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.util.Counter": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 1198}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 52, "signature": 0, "bases": 0, "doc": 29}, "controlpi_plugins.util.Counter.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.util.Counter.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.util.Date": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 648}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 29, "signature": 0, "bases": 0, "doc": 34}, "controlpi_plugins.util.Date.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.util.Date.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.wait": {"qualname": 0, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 714}, "controlpi_plugins.wait.Wait": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 590}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 28, "signature": 0, "bases": 0, "doc": 28}, "controlpi_plugins.wait.Wait.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.wait.Wait.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.wait.GenericWait": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 623}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 19}, "controlpi_plugins.wait.GenericWait.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.wait.GenericWait.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.wait.Timer": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 736}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 28, "signature": 0, "bases": 0, "doc": 28}, "controlpi_plugins.wait.Timer.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.wait.Timer.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 7}, "controlpi_plugins.wait.Periodic": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 292}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 44, "signature": 0, "bases": 0, "doc": 38}, "controlpi_plugins.wait.Periodic.process_conf": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 8}, "controlpi_plugins.wait.Periodic.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 6}}, "length": 119, "save": true}, "index": {"qualname": {"root": {"docs": {"controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 5, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 1}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}, "controlpi.baseplugin.BasePlugin.conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 36, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin.ConfException": {"tf": 1}}, "df": 1}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}}, "df": 4}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}}, "df": 3}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}}, "df": 1}}}}}}}}}, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 1}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 19}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}}, "df": 2}}}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 2}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}}, "df": 4, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}}, "df": 4}}}}}}}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 19}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 2}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}}, "df": 8}}}}}}}}, "s": {"docs": {"controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}}, "df": 1}}}}}}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}}, "df": 4}}}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}}, "df": 2}}}}}}}}}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.baseplugin.PluginConf": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 2}}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 17}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 4}}}}}}}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}, "controlpi.baseplugin.BasePlugin.bus": {"tf": 1}, "controlpi.baseplugin.BasePlugin.name": {"tf": 1}, "controlpi.baseplugin.BasePlugin.conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}}, "df": 7}}}}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin.BasePlugin.bus": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.BusException": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.BasePlugin.name": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 6, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageValue": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.messagebus.MessageCallback": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 6}}}}}}}}, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 7}}}}}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.validate": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}}, "df": 9}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}}, "df": 2}}}}}, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.MessageBus.unregister": {"tf": 1}}, "df": 1}}}}}}}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 1}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}}, "df": 4}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}}, "df": 3}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}}, "df": 4}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}}, "df": 4}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}}, "df": 4}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}}, "df": 4}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}}, "df": 4}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}}, "df": 4}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}}, "df": 4}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}}, "df": 4}}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}}, "df": 4}}}}}}, "fullname": {"root": {"docs": {"controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 5, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {"controlpi": {"tf": 1}, "controlpi.CONF_SCHEMA": {"tf": 1}, "controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.baseplugin.PluginConf": {"tf": 1}, "controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}, "controlpi.baseplugin.BasePlugin.bus": {"tf": 1}, "controlpi.baseplugin.BasePlugin.name": {"tf": 1}, "controlpi.baseplugin.BasePlugin.conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}, "controlpi.messagebus.MessageCallback": {"tf": 1}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 119}}}}}}, "f": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 1}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}, "controlpi.baseplugin.BasePlugin.conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 36, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin.ConfException": {"tf": 1}}, "df": 1}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}}, "df": 4}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}}, "df": 3}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}}, "df": 1}}}}}}}}}, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 1}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 19}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}}, "df": 2}}}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 2}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.process_conf": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.run": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}}, "df": 25, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}}, "df": 4}}}}}}}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 19}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 2}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}}, "df": 8}}}}}}}}, "s": {"docs": {"controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}}, "df": 1}}}}}}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}}, "df": 4}}}}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.baseplugin.PluginConf": {"tf": 1}, "controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.bus": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.name": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.conf": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1.4142135623730951}}, "df": 11}}}}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin.BasePlugin.bus": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.BusException": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}}, "df": 2}}}}}}}}}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.baseplugin.PluginConf": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.4142135623730951}}, "df": 3}}}}}}}}, "s": {"docs": {"controlpi_plugins": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 68}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 17}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 4}}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.BasePlugin.name": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 6, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}, "controlpi.messagebus.MessageCallback": {"tf": 1}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.run": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}}, "df": 33}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageValue": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.messagebus.MessageCallback": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 6}}}}}}}}}}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.validate": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}}, "df": 9}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}}, "df": 2}}}}}, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.MessageBus.unregister": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {"controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}}, "df": 25}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 1}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}}, "df": 4}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}}, "df": 3}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}}, "df": 4}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}}, "df": 4}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}}, "df": 4}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}}, "df": 4}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}}, "df": 4}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}}, "df": 4}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}}, "df": 4}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}}, "df": 4}}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait.run": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 17}}}}}}, "annotation": {"root": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 2.8284271247461903}}, "df": 1, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 1}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}}}}}}}, "default_value": {"root": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 3}, "controlpi.baseplugin.JSONSchema": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageValue": {"tf": 2.449489742783178}, "controlpi.messagebus.JSONSchema": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageCallback": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 3.1622776601683795}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 3.605551275463989}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 3.605551275463989}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 4.123105625617661}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 4.123105625617661}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 3.605551275463989}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 3.605551275463989}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 6.164414002968976}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 3.872983346207417}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 2.8284271247461903}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 3.1622776601683795}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 3.1622776601683795}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 3.7416573867739413}}, "df": 18, "x": {"2": {"7": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 3.4641016151377544}, "controlpi.messagebus.MessageCallback": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 3.4641016151377544}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 4.242640687119285}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 4.242640687119285}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 5.0990195135927845}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 5.0990195135927845}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 4.242640687119285}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 4.242640687119285}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 7.483314773547883}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 4.69041575982343}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 4.47213595499958}}, "df": 15}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 2.8284271247461903}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 14}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 2}, "controlpi.baseplugin.PluginConf": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageValue": {"tf": 1.7320508075688772}, "controlpi.messagebus.JSONSchema": {"tf": 2}, "controlpi.messagebus.MessageCallback": {"tf": 1.7320508075688772}}, "df": 5}}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}}, "df": 3}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}}}}}, "o": {"docs": {"controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 1}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 2}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 6}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 2}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 13}}}}}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1.4142135623730951}}, "df": 3}}}}, "d": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1.4142135623730951}, "controlpi.baseplugin.PluginConf": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1.4142135623730951}}, "df": 4}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 2}}}}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}}, "df": 3, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageCallback": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 3}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}}, "df": 3, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 8}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 2, "s": {"docs": {"controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 4}}}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 3}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}}, "df": 3}, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 2}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 7}}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}}, "df": 3}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 1, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.7320508075688772}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1.4142135623730951}, "controlpi.baseplugin.PluginConf": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1.4142135623730951}, "controlpi.messagebus.JSONSchema": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageCallback": {"tf": 1.4142135623730951}}, "df": 5, "o": {"docs": {}, "df": 0, "f": {"docs": {"controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 7}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.baseplugin.JSONSchema": {"tf": 1}, "controlpi.messagebus.MessageValue": {"tf": 1}, "controlpi.messagebus.JSONSchema": {"tf": 1}}, "df": 3}}}}}}}}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.messagebus.MessageCallback": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.MessageCallback": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "m": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageCallback": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 2, "s": {"docs": {"controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 12}}}}}}}}, "y": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 2}, "h": {"docs": {}, "df": 0, ":": {"docs": {}, "df": 0, "%": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, ":": {"docs": {}, "df": 0, "%": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 2}}}}}}}}}, "signature": {"root": {"0": {"docs": {"controlpi.test": {"tf": 1.4142135623730951}}, "df": 1}, "docs": {"controlpi.run": {"tf": 5.830951894845301}, "controlpi.test": {"tf": 8.48528137423857}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 3.4641016151377544}, "controlpi.baseplugin.BasePlugin.run": {"tf": 3.4641016151377544}, "controlpi.messagebus.register_schema": {"tf": 8.660254037844387}, "controlpi.messagebus.validate": {"tf": 8.426149773176359}, "controlpi.messagebus.Message.__init__": {"tf": 9.273618495495704}, "controlpi.messagebus.Message.check_value": {"tf": 7.874007874011811}, "controlpi.messagebus.Message.update": {"tf": 4.898979485566356}, "controlpi.messagebus.Message.setdefault": {"tf": 11.269427669584644}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 9.486832980505138}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 5.744562646538029}, "controlpi.messagebus.MessageTemplate.update": {"tf": 4.898979485566356}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 12.569805089976535}, "controlpi.messagebus.MessageTemplate.check": {"tf": 5.291502622129181}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 9.486832980505138}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 4.47213595499958}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 6}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 5.744562646538029}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 8}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 5.744562646538029}, "controlpi.messagebus.MessageBus.__init__": {"tf": 2}, "controlpi.messagebus.MessageBus.register": {"tf": 10.723805294763608}, "controlpi.messagebus.MessageBus.unregister": {"tf": 4.47213595499958}, "controlpi.messagebus.MessageBus.run": {"tf": 3.4641016151377544}, "controlpi.messagebus.MessageBus.send": {"tf": 5.291502622129181}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 5.291502622129181}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 4.47213595499958}, "controlpi_plugins.state.State.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.state.State.run": {"tf": 3.4641016151377544}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.state.StateAlias.run": {"tf": 3.4641016151377544}, "controlpi_plugins.state.AndState.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.state.AndState.run": {"tf": 3.4641016151377544}, "controlpi_plugins.state.OrState.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.state.OrState.run": {"tf": 3.4641016151377544}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.state.AndSet.run": {"tf": 3.4641016151377544}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.state.OrSet.run": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Log.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Log.run": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Init.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Init.run": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Execute.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Execute.run": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Alias.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Alias.run": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Counter.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Counter.run": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Date.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Date.run": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Wait.run": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.GenericWait.run": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Timer.run": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 3.4641016151377544}, "controlpi_plugins.wait.Periodic.run": {"tf": 3.4641016151377544}}, "df": 61, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}}, "df": 2}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 10}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 3}}}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}}, "df": 6}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 3}}}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.run": {"tf": 1.4142135623730951}, "controlpi.test": {"tf": 1.7320508075688772}, "controlpi.messagebus.register_schema": {"tf": 1.4142135623730951}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}}, "df": 9}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.run": {"tf": 1.4142135623730951}, "controlpi.test": {"tf": 1.7320508075688772}, "controlpi.messagebus.register_schema": {"tf": 1.7320508075688772}, "controlpi.messagebus.validate": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.__init__": {"tf": 2}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.setdefault": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2.6457513110645907}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 17, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.validate": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 50}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.Message.__init__": {"tf": 1}}, "df": 1}}, "s": {"docs": {"controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1.4142135623730951}, "controlpi.messagebus.register_schema": {"tf": 1.4142135623730951}, "controlpi.messagebus.validate": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}}, "df": 12}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 51, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 3}}}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 1}}}}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}}, "df": 9, "s": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 10}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}}, "df": 4}}}}}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}}, "df": 11}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}}, "df": 8}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus.register_schema": {"tf": 1.7320508075688772}, "controlpi.messagebus.validate": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}}, "df": 10}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}}, "df": 7}, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}}, "df": 2}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageBus.register": {"tf": 1.7320508075688772}}, "df": 1}}}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}}, "df": 4}}}}}, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}}, "df": 2}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 1}}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 1}}}}}}}}}}, "bases": {"root": {"docs": {"controlpi.messagebus.Message": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate": {"tf": 2.6457513110645907}}, "df": 2, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}}, "df": 2}}}}}}}, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Timer": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic": {"tf": 1.4142135623730951}}, "df": 16}}}}}}}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}}, "df": 2}}}}}}}}}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "c": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 2}}, "n": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}}, "df": 2}}}, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.Message": {"tf": 2}, "controlpi.messagebus.MessageTemplate": {"tf": 2.23606797749979}}, "df": 2}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.7320508075688772}}, "df": 2}}}}}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 2}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 2}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 2}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 2}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 2}}}}}}}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 1}}}}}}}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {"controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 16}}}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 1}}}}}}}}}, "doc": {"root": {"0": {"1": {"5": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 1}, "docs": {"controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 5}, "2": {"5": {"docs": {"controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 4}, "docs": {"controlpi_plugins.wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1.4142135623730951}}, "df": 3}, "docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 2}, "controlpi_plugins.wait.Wait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.GenericWait": {"tf": 2.23606797749979}, "controlpi_plugins.wait.Timer": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic": {"tf": 1.4142135623730951}}, "df": 13, "x": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}}, "df": 1}}, "1": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.messagebus": {"tf": 3.872983346207417}, "controlpi.messagebus.Message": {"tf": 2.8284271247461903}, "controlpi.messagebus.Message.__init__": {"tf": 2}, "controlpi.messagebus.Message.check_value": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.update": {"tf": 2.449489742783178}, "controlpi.messagebus.Message.setdefault": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate": {"tf": 2}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2.6457513110645907}, "controlpi.messagebus.TemplateRegistry": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 3.7416573867739413}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.send": {"tf": 3}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 3}, "controlpi_plugins.state.AndState": {"tf": 3}, "controlpi_plugins.state.OrState": {"tf": 3}, "controlpi_plugins.state.AndSet": {"tf": 3}, "controlpi_plugins.state.OrSet": {"tf": 3}}, "df": 26, "j": {"docs": {"controlpi.messagebus.Message.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.setdefault": {"tf": 1}}, "df": 2}}, "2": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.Message": {"tf": 2.8284271247461903}, "controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 2}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2.6457513110645907}, "controlpi.messagebus.TemplateRegistry": {"tf": 6.244997998398398}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 3.1622776601683795}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 2.6457513110645907}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2}, "controlpi.messagebus.MessageBus": {"tf": 3.3166247903554}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2.8284271247461903}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 2}, "controlpi_plugins.state.AndState": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrState": {"tf": 2.449489742783178}, "controlpi_plugins.state.AndSet": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrSet": {"tf": 2.449489742783178}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 28}, "3": {"9": {"docs": {"controlpi.run": {"tf": 4.47213595499958}, "controlpi.test": {"tf": 10.392304845413264}, "controlpi.baseplugin": {"tf": 10.099504938362077}, "controlpi.baseplugin.BasePlugin": {"tf": 7.874007874011811}, "controlpi.messagebus": {"tf": 11.74734012447073}, "controlpi.messagebus.Message": {"tf": 9.273618495495704}, "controlpi.messagebus.Message.__init__": {"tf": 4.47213595499958}, "controlpi.messagebus.Message.check_value": {"tf": 4.47213595499958}, "controlpi.messagebus.Message.update": {"tf": 7.483314773547883}, "controlpi.messagebus.Message.setdefault": {"tf": 5.0990195135927845}, "controlpi.messagebus.MessageTemplate": {"tf": 7.0710678118654755}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 3.4641016151377544}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 8.246211251235321}, "controlpi.messagebus.MessageTemplate.update": {"tf": 11.832159566199232}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 8.366600265340756}, "controlpi.messagebus.MessageTemplate.check": {"tf": 12.489995996796797}, "controlpi.messagebus.TemplateRegistry": {"tf": 24.819347291981714}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 7.615773105863909}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 7.874007874011811}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 11.489125293076057}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 8.94427190999916}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 10.862780491200215}, "controlpi.messagebus.MessageBus": {"tf": 15.620499351813308}, "controlpi.messagebus.MessageBus.register": {"tf": 5.656854249492381}, "controlpi.messagebus.MessageBus.unregister": {"tf": 4.242640687119285}, "controlpi.messagebus.MessageBus.send": {"tf": 9.486832980505138}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 9.486832980505138}, "controlpi.pluginregistry": {"tf": 3.7416573867739413}, "controlpi.pluginregistry.PluginRegistry": {"tf": 3.4641016151377544}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 2.449489742783178}, "controlpi_plugins.state": {"tf": 12.806248474865697}, "controlpi_plugins.state.State": {"tf": 9.38083151964686}, "controlpi_plugins.state.StateAlias": {"tf": 11.40175425099138}, "controlpi_plugins.state.AndState": {"tf": 11.832159566199232}, "controlpi_plugins.state.OrState": {"tf": 11.40175425099138}, "controlpi_plugins.state.AndSet": {"tf": 14.628738838327793}, "controlpi_plugins.state.OrSet": {"tf": 12.569805089976535}, "controlpi_plugins.util": {"tf": 11.74734012447073}, "controlpi_plugins.util.Log": {"tf": 8.94427190999916}, "controlpi_plugins.util.Init": {"tf": 10.488088481701515}, "controlpi_plugins.util.Execute": {"tf": 10.677078252031311}, "controlpi_plugins.util.Alias": {"tf": 14.142135623730951}, "controlpi_plugins.util.Counter": {"tf": 14.7648230602334}, "controlpi_plugins.util.Date": {"tf": 10.583005244258363}, "controlpi_plugins.wait": {"tf": 12}, "controlpi_plugins.wait.Wait": {"tf": 10.770329614269007}, "controlpi_plugins.wait.GenericWait": {"tf": 10.770329614269007}, "controlpi_plugins.wait.Timer": {"tf": 11.916375287812984}, "controlpi_plugins.wait.Periodic": {"tf": 6.48074069840786}}, "df": 49}, "docs": {"controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry": {"tf": 2.23606797749979}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 3.1622776601683795}, "controlpi_plugins.state.OrSet": {"tf": 2.449489742783178}, "controlpi_plugins.util.Counter": {"tf": 1}}, "df": 12}, "4": {"2": {"docs": {"controlpi.run": {"tf": 2.449489742783178}, "controlpi.test": {"tf": 3.4641016151377544}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 3.1622776601683795}, "controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 3.7416573867739413}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}, "controlpi_plugins.util": {"tf": 2.23606797749979}, "controlpi_plugins.util.Log": {"tf": 3.605551275463989}, "controlpi_plugins.util.Init": {"tf": 3.605551275463989}, "controlpi_plugins.util.Execute": {"tf": 3}, "controlpi_plugins.util.Alias": {"tf": 3.7416573867739413}, "controlpi_plugins.util.Counter": {"tf": 4.242640687119285}}, "df": 19}, "9": {"docs": {"controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}}, "df": 1}, "docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 2}}, "df": 5}, "5": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 2.8284271247461903}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}}, "df": 5}, "6": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 2.8284271247461903}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1.7320508075688772}}, "df": 2}, "7": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1.7320508075688772}}, "df": 1}, "docs": {"controlpi": {"tf": 3.4641016151377544}, "controlpi.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi.run": {"tf": 18.601075237738275}, "controlpi.test": {"tf": 19.183326093250876}, "controlpi.baseplugin": {"tf": 33.04542328371661}, "controlpi.baseplugin.JSONSchema": {"tf": 1.7320508075688772}, "controlpi.baseplugin.PluginConf": {"tf": 1.7320508075688772}, "controlpi.baseplugin.ConfException": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin": {"tf": 27.349588662354687}, "controlpi.baseplugin.BasePlugin.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.bus": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.name": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.conf": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 2.449489742783178}, "controlpi.baseplugin.BasePlugin.run": {"tf": 2.449489742783178}, "controlpi.messagebus": {"tf": 27.910571473905726}, "controlpi.messagebus.MessageValue": {"tf": 1.7320508075688772}, "controlpi.messagebus.JSONSchema": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageCallback": {"tf": 1.7320508075688772}, "controlpi.messagebus.register_schema": {"tf": 1.7320508075688772}, "controlpi.messagebus.validate": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message": {"tf": 19.8997487421324}, "controlpi.messagebus.Message.__init__": {"tf": 10.295630140987}, "controlpi.messagebus.Message.check_value": {"tf": 21.166010488516726}, "controlpi.messagebus.Message.update": {"tf": 18.24828759089466}, "controlpi.messagebus.Message.setdefault": {"tf": 14.560219778561036}, "controlpi.messagebus.MessageTemplate": {"tf": 17.944358444926362}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 9.899494936611665}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 16.55294535724685}, "controlpi.messagebus.MessageTemplate.update": {"tf": 25.337718918639855}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 18.627936010197157}, "controlpi.messagebus.MessageTemplate.check": {"tf": 34.02939905434711}, "controlpi.messagebus.TemplateRegistry": {"tf": 46.87216658103186}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 5.291502622129181}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 16.97056274847714}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 18.110770276274835}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 22.22611077089287}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 17.26267650163207}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 22.090722034374522}, "controlpi.messagebus.BusException": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 31.368774282716245}, "controlpi.messagebus.MessageBus.__init__": {"tf": 7.681145747868608}, "controlpi.messagebus.MessageBus.register": {"tf": 19.209372712298546}, "controlpi.messagebus.MessageBus.unregister": {"tf": 14.628738838327793}, "controlpi.messagebus.MessageBus.run": {"tf": 11.789826122551595}, "controlpi.messagebus.MessageBus.send": {"tf": 24.576411454889016}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 24.454038521274967}, "controlpi.pluginregistry": {"tf": 16.24807680927192}, "controlpi.pluginregistry.PluginRegistry": {"tf": 15.033296378372908}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 13}, "controlpi_plugins": {"tf": 1.7320508075688772}, "controlpi_plugins.state": {"tf": 29}, "controlpi_plugins.state.State": {"tf": 18.027756377319946}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 2.449489742783178}, "controlpi_plugins.state.State.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.state.State.run": {"tf": 1.7320508075688772}, "controlpi_plugins.state.StateAlias": {"tf": 20.174241001832016}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.state.StateAlias.run": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndState": {"tf": 22.090722034374522}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndState.run": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrState": {"tf": 21.93171219946131}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrState.run": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndSet": {"tf": 25.13961017995307}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 4.358898943540674}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndSet.run": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrSet": {"tf": 23.473389188611005}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 4.358898943540674}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrSet.run": {"tf": 1.7320508075688772}, "controlpi_plugins.util": {"tf": 21}, "controlpi_plugins.util.Log": {"tf": 20.493901531919196}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.util.Log.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log.run": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init": {"tf": 21.18962010041709}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.util.Init.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init.run": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Execute": {"tf": 18.520259177452136}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 2.449489742783178}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Execute.run": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias": {"tf": 30.331501776206203}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 5.477225575051661}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias.run": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Counter": {"tf": 25.337718918639855}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Counter.run": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Date": {"tf": 18.65475810617763}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.util.Date.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Date.run": {"tf": 1.7320508075688772}, "controlpi_plugins.wait": {"tf": 19.078784028338912}, "controlpi_plugins.wait.Wait": {"tf": 17.435595774162696}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Wait.run": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.GenericWait": {"tf": 17.86057109949175}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 2.449489742783178}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Timer": {"tf": 19.519221295943137}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 3.7416573867739413}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Timer.run": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Periodic": {"tf": 12.84523257866513}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 4.358898943540674}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Periodic.run": {"tf": 1.7320508075688772}}, "df": 119, "p": {"1": {"docs": {"controlpi.pluginregistry": {"tf": 1.4142135623730951}}, "df": 1}, "docs": {"controlpi.baseplugin": {"tf": 2}, "controlpi.baseplugin.BasePlugin": {"tf": 2.449489742783178}}, "df": 2, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}}, "df": 9, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}}, "df": 2}}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}}, "df": 5, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}}, "df": 2}}}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 11}}}, "y": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}}, "df": 14}}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 2}, "controlpi.baseplugin.BasePlugin": {"tf": 2.6457513110645907}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message": {"tf": 2.23606797749979}, "controlpi.messagebus.Message.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 3}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 21, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 4}}, "r": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}}, "df": 1}}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 1}}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"1": {"docs": {"controlpi.pluginregistry": {"tf": 2.6457513110645907}, "controlpi.pluginregistry.PluginRegistry": {"tf": 2.6457513110645907}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.7320508075688772}}, "df": 3}, "2": {"docs": {"controlpi.pluginregistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry": {"tf": 2}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.7320508075688772}}, "df": 3}, "docs": {"controlpi": {"tf": 1.4142135623730951}, "controlpi.run": {"tf": 1.4142135623730951}, "controlpi.test": {"tf": 2.23606797749979}, "controlpi.baseplugin": {"tf": 3.7416573867739413}, "controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus": {"tf": 3.872983346207417}, "controlpi.messagebus.MessageBus.register": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 3}, "controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1.7320508075688772}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 2}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 2}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 2}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 2}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util": {"tf": 2.449489742783178}, "controlpi_plugins.util.Log": {"tf": 2.23606797749979}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 2}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 2.6457513110645907}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 2.23606797749979}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait": {"tf": 2}, "controlpi_plugins.wait.Wait": {"tf": 2}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 64, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi": {"tf": 1}, "controlpi.pluginregistry": {"tf": 2}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.7320508075688772}}, "df": 4}}}}}}}}, "s": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1.7320508075688772}, "controlpi.baseplugin": {"tf": 2.23606797749979}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.7320508075688772}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}}, "df": 13}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.4142135623730951}}, "df": 4, "s": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.7320508075688772}}, "df": 10}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}}, "df": 6}}}, "r": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}}, "df": 1, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}}, "df": 3}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1}}, "df": 2}, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.Message.__init__": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "c": {"docs": {"controlpi_plugins.wait.Periodic": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 2, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 2}}}}}}}}}}}}, "t": {"docs": {"controlpi.messagebus.MessageTemplate": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 2}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2}, "controlpi.messagebus.MessageTemplate.update": {"tf": 3.3166247903554}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageTemplate.check": {"tf": 4.47213595499958}}, "df": 6, "h": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 2.8284271247461903}, "controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 2}, "controlpi.baseplugin": {"tf": 5.477225575051661}, "controlpi.baseplugin.BasePlugin": {"tf": 2.6457513110645907}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 2.23606797749979}, "controlpi.baseplugin.BasePlugin.run": {"tf": 2.23606797749979}, "controlpi.messagebus": {"tf": 5.196152422706632}, "controlpi.messagebus.register_schema": {"tf": 1.4142135623730951}, "controlpi.messagebus.validate": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 2.8284271247461903}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 3.1622776601683795}, "controlpi.messagebus.MessageBus": {"tf": 4.242640687119285}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 2.8284271247461903}, "controlpi.pluginregistry.PluginRegistry": {"tf": 2.449489742783178}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.7320508075688772}, "controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.state.State": {"tf": 2.23606797749979}, "controlpi_plugins.state.StateAlias": {"tf": 2.449489742783178}, "controlpi_plugins.state.AndState": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrState": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndSet": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrSet": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Init": {"tf": 2.8284271247461903}, "controlpi_plugins.util.Execute": {"tf": 2.6457513110645907}, "controlpi_plugins.util.Alias": {"tf": 4.47213595499958}, "controlpi_plugins.util.Counter": {"tf": 2.449489742783178}, "controlpi_plugins.util.Date": {"tf": 2}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.GenericWait": {"tf": 2}, "controlpi_plugins.wait.Timer": {"tf": 2}, "controlpi_plugins.wait.Periodic": {"tf": 2}}, "df": 44, "m": {"docs": {"controlpi": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}}, "df": 2}, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 1.7320508075688772}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 2}}, "df": 5}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}}, "df": 4, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 2}}}}}}, "y": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.state": {"tf": 2.23606797749979}}, "df": 3}, "i": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.7320508075688772}}, "df": 4}}, "n": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}}, "df": 3}}, "i": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 2.23606797749979}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1}}, "df": 13}, "r": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.util.Log": {"tf": 2}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 10}}}, "o": {"docs": {"controlpi": {"tf": 1.4142135623730951}, "controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 2.23606797749979}, "controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 4}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 3.3166247903554}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 2.449489742783178}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrState": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 2}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 4.242640687119285}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 2}, "controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 47}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi": {"tf": 1.4142135623730951}, "controlpi.run": {"tf": 2}, "controlpi.test": {"tf": 4.123105625617661}, "controlpi.baseplugin": {"tf": 4}, "controlpi.baseplugin.BasePlugin": {"tf": 3.4641016151377544}, "controlpi.messagebus": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus": {"tf": 3.3166247903554}, "controlpi.messagebus.MessageBus.register": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 2}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2}, "controlpi_plugins.state": {"tf": 7.874007874011811}, "controlpi_plugins.state.State": {"tf": 5.196152422706632}, "controlpi_plugins.state.StateAlias": {"tf": 6.244997998398398}, "controlpi_plugins.state.AndState": {"tf": 6.557438524302}, "controlpi_plugins.state.OrState": {"tf": 6.4031242374328485}, "controlpi_plugins.state.AndSet": {"tf": 7.937253933193772}, "controlpi_plugins.state.OrSet": {"tf": 7}, "controlpi_plugins.util": {"tf": 5.0990195135927845}, "controlpi_plugins.util.Log": {"tf": 5}, "controlpi_plugins.util.Init": {"tf": 5.291502622129181}, "controlpi_plugins.util.Execute": {"tf": 4.795831523312719}, "controlpi_plugins.util.Alias": {"tf": 6.244997998398398}, "controlpi_plugins.util.Counter": {"tf": 7.3484692283495345}, "controlpi_plugins.util.Date": {"tf": 4.69041575982343}, "controlpi_plugins.wait": {"tf": 4.58257569495584}, "controlpi_plugins.wait.Wait": {"tf": 3}, "controlpi_plugins.wait.GenericWait": {"tf": 4.123105625617661}, "controlpi_plugins.wait.Timer": {"tf": 3.872983346207417}, "controlpi_plugins.wait.Periodic": {"tf": 2}}, "df": 30, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin": {"tf": 2.23606797749979}}, "df": 2}}}}}}}}, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate": {"tf": 2}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}}, "df": 14, "s": {"docs": {"controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.TemplateRegistry": {"tf": 2.8284271247461903}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}}, "df": 9}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}}, "df": 7}}}}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.run": {"tf": 2}, "controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.run": {"tf": 2}, "controlpi.messagebus.MessageBus.send": {"tf": 2}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2}}, "df": 7, "s": {"docs": {"controlpi.baseplugin.BasePlugin.run": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1.7320508075688772}, "controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus": {"tf": 3.7416573867739413}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2.8284271247461903}, "controlpi_plugins.state": {"tf": 3.7416573867739413}, "controlpi_plugins.state.State": {"tf": 2.8284271247461903}, "controlpi_plugins.state.StateAlias": {"tf": 3.1622776601683795}, "controlpi_plugins.state.AndState": {"tf": 3.1622776601683795}, "controlpi_plugins.state.OrState": {"tf": 3.1622776601683795}, "controlpi_plugins.state.AndSet": {"tf": 4}, "controlpi_plugins.state.OrSet": {"tf": 3.4641016151377544}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 2}, "controlpi_plugins.util.Execute": {"tf": 2.449489742783178}, "controlpi_plugins.util.Counter": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Date": {"tf": 2.449489742783178}, "controlpi_plugins.wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.GenericWait": {"tf": 2.23606797749979}, "controlpi_plugins.wait.Timer": {"tf": 3.4641016151377544}}, "df": 24}}}}}, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}}, "df": 7}, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 3}, "controlpi.messagebus.MessageTemplate": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 3.7416573867739413}, "controlpi.messagebus.TemplateRegistry": {"tf": 3.7416573867739413}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 2}, "controlpi_plugins.state": {"tf": 3.1622776601683795}, "controlpi_plugins.state.State": {"tf": 2.6457513110645907}, "controlpi_plugins.state.StateAlias": {"tf": 3}, "controlpi_plugins.state.AndState": {"tf": 2.8284271247461903}, "controlpi_plugins.state.OrState": {"tf": 2.8284271247461903}, "controlpi_plugins.state.AndSet": {"tf": 3.1622776601683795}, "controlpi_plugins.state.OrSet": {"tf": 2.8284271247461903}}, "df": 17}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}}, "df": 5}}}}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Alias": {"tf": 2.6457513110645907}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}}, "df": 2, "s": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.util": {"tf": 1}}, "df": 2}, "d": {"docs": {"controlpi_plugins.util": {"tf": 2}, "controlpi_plugins.util.Alias": {"tf": 3}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.7320508075688772}}, "df": 3}}}}}}}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 3.4641016151377544}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2.6457513110645907}, "controlpi.messagebus.TemplateRegistry": {"tf": 2.23606797749979}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.GenericWait": {"tf": 1.7320508075688772}}, "df": 24, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 3}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}}, "df": 4}}}}}}}}, "w": {"docs": {}, "df": 0, "o": {"docs": {"controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 3}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Init": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}}, "df": 4, "r": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 4.47213595499958}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}}, "df": 2}}}}}, "i": {"1": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}, "docs": {"controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 3, "n": {"docs": {"controlpi": {"tf": 1}, "controlpi.baseplugin": {"tf": 2}, "controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.update": {"tf": 2.23606797749979}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 3.1622776601683795}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 2.449489742783178}, "controlpi.pluginregistry.PluginRegistry": {"tf": 2.8284271247461903}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 2}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Execute": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 35, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}}, "df": 4}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.BasePlugin.run": {"tf": 1}}, "df": 1}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}}, "df": 3}}}}}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}, "s": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.run": {"tf": 2.23606797749979}, "controlpi.test": {"tf": 3.872983346207417}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi_plugins.util": {"tf": 3}, "controlpi_plugins.util.Init": {"tf": 4.123105625617661}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}}, "df": 7, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 4}}}}}, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 7, "d": {"docs": {"controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}}, "df": 3}}}}}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 2}, "controlpi.baseplugin.BasePlugin": {"tf": 2.6457513110645907}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1.7320508075688772}}, "df": 7, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.7320508075688772}}, "df": 2}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 2.8284271247461903}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 2.23606797749979}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 2.23606797749979}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2.6457513110645907}}, "df": 6}}}}, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}}, "df": 5, "/": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}}}}}, "t": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 7, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}}, "df": 9, "s": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 3}}}}, "r": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 3}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 1}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}}, "df": 1}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "t": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 8, "s": {"docs": {"controlpi": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}}, "df": 2}}}}, "s": {"docs": {"controlpi": {"tf": 1.4142135623730951}, "controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.baseplugin.BasePlugin": {"tf": 2}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 2.449489742783178}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 2.23606797749979}, "controlpi.messagebus.Message.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 2}, "controlpi_plugins.util.Init": {"tf": 2}, "controlpi_plugins.util.Alias": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 29}, "d": {"docs": {"controlpi.run": {"tf": 2}, "controlpi.test": {"tf": 2.8284271247461903}, "controlpi_plugins.util": {"tf": 3}, "controlpi_plugins.util.Log": {"tf": 3.1622776601683795}, "controlpi_plugins.util.Init": {"tf": 2.8284271247461903}, "controlpi_plugins.util.Execute": {"tf": 2.449489742783178}, "controlpi_plugins.util.Alias": {"tf": 4.58257569495584}, "controlpi_plugins.util.Counter": {"tf": 4.47213595499958}, "controlpi_plugins.wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.GenericWait": {"tf": 3}}, "df": 10}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 22, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {"controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 3}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "f": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 2.449489742783178}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 14}}, "f": {"docs": {"controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 3}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}}, "df": 13, "o": {"docs": {}, "df": 0, "r": {"docs": {"controlpi": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 3}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2.23606797749979}, "controlpi.messagebus.TemplateRegistry": {"tf": 3.872983346207417}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 4.358898943540674}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 2}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 49, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}}, "df": 3}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.state.StateAlias": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.util.Date": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 2}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}}, "df": 2}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"controlpi": {"tf": 1.7320508075688772}, "controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 3.605551275463989}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}}, "df": 15}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi": {"tf": 1.7320508075688772}, "controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}}, "df": 7, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}}, "df": 3}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.run": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 2.449489742783178}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}}, "df": 4, "[": {"0": {"docs": {"controlpi_plugins.util.Log": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Wait": {"tf": 2.23606797749979}, "controlpi_plugins.wait.GenericWait": {"tf": 2}, "controlpi_plugins.wait.Timer": {"tf": 2.23606797749979}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 5}}}}}}, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.7320508075688772}}, "df": 4}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 4, "s": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2}}, "df": 3}}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry": {"tf": 4}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 2}, "controlpi_plugins.state": {"tf": 2.23606797749979}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 2}, "controlpi_plugins.state.OrState": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndSet": {"tf": 2.6457513110645907}, "controlpi_plugins.state.OrSet": {"tf": 2.23606797749979}}, "df": 11}}}}}, "c": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 6.6332495807108}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 2.23606797749979}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 2.6457513110645907}}, "df": 3, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {"controlpi": {"tf": 1.4142135623730951}, "controlpi.run": {"tf": 1.4142135623730951}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.util": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 2}, "controlpi_plugins.util.Init": {"tf": 2}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 2.449489742783178}, "controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date": {"tf": 1.7320508075688772}, "controlpi_plugins.wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Timer": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic": {"tf": 1.4142135623730951}}, "df": 24}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.run": {"tf": 2}, "controlpi.test": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi_plugins.util": {"tf": 2.23606797749979}, "controlpi_plugins.util.Init": {"tf": 2.8284271247461903}, "controlpi_plugins.util.Execute": {"tf": 2.449489742783178}, "controlpi_plugins.util.Alias": {"tf": 5}}, "df": 7}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.test": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}}, "df": 4, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi_plugins.state": {"tf": 2.23606797749979}}, "df": 2}}}, "s": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"controlpi": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 2.449489742783178}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry": {"tf": 3.605551275463989}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 3.4641016151377544}, "controlpi.messagebus.MessageBus": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}, "controlpi_plugins.util": {"tf": 3.3166247903554}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 2.449489742783178}, "controlpi_plugins.util.Execute": {"tf": 2}, "controlpi_plugins.util.Alias": {"tf": 2.8284271247461903}, "controlpi_plugins.util.Counter": {"tf": 2.449489742783178}, "controlpi_plugins.util.Date": {"tf": 2}, "controlpi_plugins.wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.GenericWait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Timer": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 33, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus": {"tf": null}, "controlpi.messagebus.Message": {"tf": null}}, "df": 2}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.Message": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.Message": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "f": {"docs": {"controlpi.run": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 3}, "controlpi.baseplugin.BasePlugin": {"tf": 3.4641016151377544}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}}, "df": 4, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 2}, "controlpi.test": {"tf": 2}, "controlpi.baseplugin": {"tf": 2.23606797749979}, "controlpi.baseplugin.BasePlugin": {"tf": 2}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 2.449489742783178}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 36, "s": {"docs": {"controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.messagebus": {"tf": 1}}, "df": 3}}}}}, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}}, "df": 4}}}}}, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}}, "df": 1}}}}}}}}}}, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1.4142135623730951}}, "df": 4}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}}, "df": 3}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"controlpi_plugins.state": {"tf": 2}}, "df": 1}}}}}}}}, "j": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1}}, "df": 3}}}}}}}}}, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1, "s": {"docs": {"controlpi": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.4142135623730951}}, "df": 2}, "d": {"docs": {"controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}}, "df": 6}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi_plugins.state": {"tf": 3.7416573867739413}, "controlpi_plugins.state.State": {"tf": 3.1622776601683795}, "controlpi_plugins.state.StateAlias": {"tf": 3.1622776601683795}, "controlpi_plugins.state.AndState": {"tf": 3.1622776601683795}, "controlpi_plugins.state.OrState": {"tf": 3.1622776601683795}, "controlpi_plugins.state.AndSet": {"tf": 4}, "controlpi_plugins.state.OrSet": {"tf": 3.4641016151377544}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 2.23606797749979}, "controlpi_plugins.util.Execute": {"tf": 3.1622776601683795}, "controlpi_plugins.util.Counter": {"tf": 3.605551275463989}, "controlpi_plugins.util.Date": {"tf": 2.449489742783178}, "controlpi_plugins.wait": {"tf": 2.8284271247461903}, "controlpi_plugins.wait.Wait": {"tf": 2.6457513110645907}, "controlpi_plugins.wait.GenericWait": {"tf": 2.6457513110645907}, "controlpi_plugins.wait.Timer": {"tf": 3.7416573867739413}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 20, "s": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}}, "df": 7}}}}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}, "x": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.setdefault": {"tf": 1}}, "df": 3}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1}, "controlpi.test": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}}, "df": 19}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.run": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}}, "df": 3, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}}, "df": 2}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 2}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus": {"tf": 1}}, "df": 2}}, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.util.Counter": {"tf": 4.242640687119285}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 3, "s": {"docs": {"controlpi_plugins.util.Counter": {"tf": 1}}, "df": 1}, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.util.Counter": {"tf": 4.69041575982343}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}}, "df": 2}, "d": {"docs": {"controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1}}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 8, "d": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 3}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 2}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 15, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 2}}, "df": 8, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 2}}, "df": 1, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 7}}}}}}}}, "s": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 4}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}}, "df": 5, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}}, "df": 4}}, "s": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}}, "df": 1}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.messagebus": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageBus": {"tf": 3.4641016151377544}, "controlpi.messagebus.MessageBus.register": {"tf": 2}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.7320508075688772}}, "df": 6, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 3}}}}}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}}, "df": 1}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.register_schema": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 4.242640687119285}, "controlpi.messagebus.TemplateRegistry": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 2.23606797749979}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 3.7416573867739413}, "controlpi.messagebus.MessageBus": {"tf": 6.324555320336759}, "controlpi.messagebus.MessageBus.register": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageBus.unregister": {"tf": 2}, "controlpi.messagebus.MessageBus.send": {"tf": 4.123105625617661}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 4.123105625617661}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 45, "s": {"docs": {"controlpi.baseplugin": {"tf": 2.449489742783178}, "controlpi.messagebus": {"tf": 3}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi_plugins.state": {"tf": 2.23606797749979}}, "df": 8}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.pluginregistry": {"tf": 3.3166247903554}, "controlpi.pluginregistry.PluginRegistry": {"tf": 2.449489742783178}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 2.449489742783178}}, "df": 5, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 3.872983346207417}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 3.872983346207417}, "controlpi.messagebus.TemplateRegistry": {"tf": 2.8284271247461903}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.4142135623730951}}, "df": 5, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 4}}, "s": {"docs": {"controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 5}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}}, "df": 3, "s": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}}, "df": 1}, "d": {"docs": {"controlpi_plugins.state": {"tf": 2.6457513110645907}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndState": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrState": {"tf": 2.23606797749979}, "controlpi_plugins.state.AndSet": {"tf": 2.23606797749979}, "controlpi_plugins.state.OrSet": {"tf": 2}}, "df": 7}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date": {"tf": 1}}, "df": 2, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 3}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 2}}}}}}}, "s": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}}, "df": 2, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {"controlpi": {"tf": 1.4142135623730951}, "controlpi.run": {"tf": 1.4142135623730951}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 2.23606797749979}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}}, "df": 6, "s": {"docs": {"controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}}, "df": 3}}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 3.3166247903554}, "controlpi_plugins.state.State": {"tf": 2.449489742783178}, "controlpi_plugins.state.StateAlias": {"tf": 2.449489742783178}, "controlpi_plugins.state.AndState": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrState": {"tf": 2.449489742783178}, "controlpi_plugins.state.AndSet": {"tf": 3.3166247903554}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 3}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 2}}, "df": 12, "u": {"docs": {}, "df": 0, "p": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 4}}, "s": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Execute": {"tf": 1}}, "df": 3}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message.setdefault": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2.6457513110645907}}, "df": 2}}}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {"controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}}, "df": 2}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.run": {"tf": 1.4142135623730951}, "controlpi.test": {"tf": 2}, "controlpi_plugins.util.Log": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init": {"tf": 2.23606797749979}, "controlpi_plugins.util.Execute": {"tf": 2}, "controlpi_plugins.util.Alias": {"tf": 2.449489742783178}}, "df": 6, "s": {"docs": {"controlpi_plugins.wait": {"tf": 2}, "controlpi_plugins.wait.Wait": {"tf": 2}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 2.6457513110645907}, "controlpi_plugins.wait.Timer": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 8}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 3.3166247903554}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 3.3166247903554}, "controlpi.messagebus.MessageBus.register": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.send": {"tf": 2}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 15, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.run": {"tf": 1.7320508075688772}, "controlpi.test": {"tf": 2.449489742783178}, "controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.messagebus": {"tf": 3.605551275463989}, "controlpi.messagebus.Message": {"tf": 4.795831523312719}, "controlpi.messagebus.Message.__init__": {"tf": 2.6457513110645907}, "controlpi.messagebus.Message.update": {"tf": 2.8284271247461903}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.check": {"tf": 3.7416573867739413}, "controlpi.messagebus.MessageBus": {"tf": 4}, "controlpi.messagebus.MessageBus.send": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2.6457513110645907}, "controlpi_plugins.state": {"tf": 3.872983346207417}, "controlpi_plugins.state.State": {"tf": 3}, "controlpi_plugins.state.StateAlias": {"tf": 3.605551275463989}, "controlpi_plugins.state.AndState": {"tf": 3.605551275463989}, "controlpi_plugins.state.OrState": {"tf": 3.4641016151377544}, "controlpi_plugins.state.AndSet": {"tf": 4.358898943540674}, "controlpi_plugins.state.OrSet": {"tf": 3.7416573867739413}, "controlpi_plugins.util": {"tf": 3.1622776601683795}, "controlpi_plugins.util.Log": {"tf": 2.449489742783178}, "controlpi_plugins.util.Init": {"tf": 2.449489742783178}, "controlpi_plugins.util.Execute": {"tf": 2.23606797749979}, "controlpi_plugins.util.Alias": {"tf": 3.1622776601683795}, "controlpi_plugins.util.Counter": {"tf": 4.47213595499958}, "controlpi_plugins.util.Date": {"tf": 2.449489742783178}, "controlpi_plugins.wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.GenericWait": {"tf": 2.23606797749979}, "controlpi_plugins.wait.Timer": {"tf": 3}, "controlpi_plugins.wait.Periodic": {"tf": 1.7320508075688772}}, "df": 33}}, "s": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 2.8284271247461903}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.util": {"tf": 2.23606797749979}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 2}, "controlpi_plugins.wait.Wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 17}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 6}}}}, "t": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 2}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 2}, "controlpi_plugins.util.Alias": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}}, "df": 8}}, "l": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.baseplugin": {"tf": 4.47213595499958}, "controlpi.baseplugin.BasePlugin": {"tf": 2.8284271247461903}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}}, "df": 3}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.7320508075688772}}, "df": 1}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 6}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.test": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 2}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Counter": {"tf": 2.23606797749979}}, "df": 1}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2}}, "df": 4, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.baseplugin": {"tf": 2}, "controlpi.baseplugin.BasePlugin": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 3}}}}}}}, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}}, "df": 4}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 3}}, "r": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 2.6457513110645907}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.GenericWait": {"tf": 1.7320508075688772}}, "df": 26, "s": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 3}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state": {"tf": 9.055385138137417}, "controlpi_plugins.state.State": {"tf": 6.082762530298219}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 6.244997998398398}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 6.164414002968976}, "controlpi_plugins.state.OrState": {"tf": 6.082762530298219}, "controlpi_plugins.state.AndSet": {"tf": 7.937253933193772}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 7.14142842854285}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 11, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state": {"tf": 3.3166247903554}, "controlpi_plugins.state.StateAlias": {"tf": 3.4641016151377544}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}}, "df": 3}}}}}, "s": {"docs": {"controlpi_plugins.state": {"tf": 2}, "controlpi_plugins.state.AndState": {"tf": 2.449489742783178}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 2.23606797749979}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 2.23606797749979}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 9}}}, "r": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 3.1622776601683795}}, "df": 1, "u": {"docs": {}, "df": 0, "p": {"docs": {"controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init.run": {"tf": 1}}, "df": 3}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}}, "df": 2}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"controlpi.baseplugin": {"tf": 2.449489742783178}, "controlpi.baseplugin.BasePlugin": {"tf": 2}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 23, "s": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}}, "df": 4}}}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}}, "df": 7}}}, "r": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.wait.Wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.GenericWait": {"tf": 1.7320508075688772}}, "df": 2}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1.7320508075688772}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}}, "df": 2}}}, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}, "b": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 3}}}}}}}}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {"controlpi": {"tf": 1}, "controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 3}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 3.4641016151377544}, "controlpi.messagebus.Message": {"tf": 2}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi_plugins.util": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias": {"tf": 2}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 41, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1.7320508075688772}, "controlpi.test": {"tf": 1.7320508075688772}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util": {"tf": 2}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}}, "df": 12, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}}, "df": 4}, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}}, "df": 5}}, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Init": {"tf": 1}}, "df": 1}}}, "r": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}}, "df": 12, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus.Message": {"tf": 1}}, "df": 1}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state": {"tf": 3}, "controlpi_plugins.state.OrState": {"tf": 3.1622776601683795}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state": {"tf": 2}, "controlpi_plugins.state.OrSet": {"tf": 3}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}}, "df": 3}}}}, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 10, "s": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 4}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}}, "df": 2}}}, "e": {"docs": {"controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}}, "df": 4}}}}, "w": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.Message": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin.BasePlugin.run": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.state.StateAlias": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}}, "df": 7, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1}}}}}}}}, "k": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin.BasePlugin.run": {"tf": 1}}, "df": 1}}}, "w": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}}, "df": 5}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 1}}}}}}}}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}}, "df": 5}}}}}}}, "l": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.util.Alias": {"tf": 3.4641016151377544}}, "df": 1}}}, "m": {"docs": {"controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message": {"tf": 3.4641016151377544}, "controlpi.messagebus.Message.__init__": {"tf": 2}, "controlpi.messagebus.Message.update": {"tf": 3}, "controlpi.messagebus.Message.setdefault": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry": {"tf": 4.898979485566356}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 11, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 2.449489742783178}, "controlpi.test": {"tf": 3.605551275463989}, "controlpi.baseplugin": {"tf": 3.1622776601683795}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 3.872983346207417}, "controlpi.messagebus.Message": {"tf": 3}, "controlpi.messagebus.Message.__init__": {"tf": 2}, "controlpi.messagebus.Message.check_value": {"tf": 3.7416573867739413}, "controlpi.messagebus.Message.update": {"tf": 2.8284271247461903}, "controlpi.messagebus.Message.setdefault": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageTemplate.check": {"tf": 4.242640687119285}, "controlpi.messagebus.TemplateRegistry": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 3.1622776601683795}, "controlpi.messagebus.MessageBus.register": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2.23606797749979}, "controlpi_plugins.state": {"tf": 2.8284271247461903}, "controlpi_plugins.util": {"tf": 2.23606797749979}, "controlpi_plugins.util.Log": {"tf": 4.47213595499958}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 3}, "controlpi_plugins.util.Execute": {"tf": 2.449489742783178}, "controlpi_plugins.util.Alias": {"tf": 4.58257569495584}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1.4142135623730951}}, "df": 39, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"controlpi": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 2.23606797749979}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 11}}}, "s": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 2.449489742783178}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 3.1622776601683795}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 2.8284271247461903}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.util": {"tf": 2.23606797749979}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 3.1622776601683795}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init.run": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 3.3166247903554}, "controlpi_plugins.util.Alias": {"tf": 2.23606797749979}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}}, "df": 22, "[": {"0": {"docs": {"controlpi_plugins.util.Init": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 3}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.register": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send": {"tf": 2}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2}}, "df": 12}}}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.validate": {"tf": 1}}, "df": 1}}}}}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 3, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 2}}}}, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1.7320508075688772}}, "df": 1, "s": {"docs": {"controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 3}}}}, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 2}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}}, "df": 6}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.run": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}}, "df": 11, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.run": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}}}}}}, "y": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 4, "s": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}}, "df": 3, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 4}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 7}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi": {"tf": 1}, "controlpi.test": {"tf": 1}}, "df": 2}}}}}, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin": {"tf": 2}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.test": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}}, "df": 4}}, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1}}}}}}}}, "b": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 1, "u": {"docs": {}, "df": 0, "s": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1.4142135623730951}, "controlpi.test": {"tf": 2.6457513110645907}, "controlpi.baseplugin": {"tf": 5.477225575051661}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 4.58257569495584}, "controlpi.messagebus.BusException": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 4.69041575982343}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.register": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.unregister": {"tf": 2}, "controlpi.messagebus.MessageBus.run": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus.send": {"tf": 3.3166247903554}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 3.3166247903554}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 33, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1.7320508075688772}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 2}}}}}}}}}}, "t": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}}, "df": 10}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 6, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin": {"tf": 2}, "controlpi.pluginregistry": {"tf": 2}, "controlpi.pluginregistry.PluginRegistry": {"tf": 2}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 2}}, "df": 6}}}}}}, "d": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}}, "df": 5}}, "i": {"docs": {}, "df": 0, "c": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "k": {"docs": {"controlpi_plugins.wait.GenericWait": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2.449489742783178}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 2}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias": {"tf": 2.6457513110645907}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}}, "df": 22, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi": {"tf": 1}, "controlpi.test": {"tf": 1}}, "df": 2}}}}}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}}, "df": 3, "s": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1}}, "df": 2}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 2}}}, "e": {"docs": {"controlpi.test": {"tf": 1.7320508075688772}, "controlpi.baseplugin": {"tf": 2.449489742783178}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.Message": {"tf": 2}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi.pluginregistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 30, "t": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 5}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 1}}}}}}}}, "r": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 4.358898943540674}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 2.8284271247461903}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 2.23606797749979}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 3.7416573867739413}}, "df": 7, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.pluginregistry": {"tf": 3}, "controlpi.pluginregistry.PluginRegistry": {"tf": 3}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 2}}, "df": 6}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 4, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.register": {"tf": 2}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 26, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.TemplateRegistry": {"tf": 2.449489742783178}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2.8284271247461903}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 27}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 1}}}}}}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 2}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.register": {"tf": 1.7320508075688772}, "controlpi_plugins.state": {"tf": 1}}, "df": 5, "s": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 2.6457513110645907}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.util": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 16}, "d": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 4}, "r": {"docs": {"controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus": {"tf": 2.6457513110645907}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 5}}}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}}, "df": 5}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}}, "t": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}}, "df": 5}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}, "l": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 1, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 20}}}}}}, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.BasePlugin.run": {"tf": 1}}, "df": 1}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 2, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.util.Counter": {"tf": 2.449489742783178}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2, "s": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}}, "df": 1}}}}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 2}}}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"controlpi": {"tf": 1.4142135623730951}, "controlpi.run": {"tf": 2.8284271247461903}, "controlpi.test": {"tf": 1.7320508075688772}, "controlpi.baseplugin": {"tf": 3.4641016151377544}, "controlpi.baseplugin.BasePlugin": {"tf": 2.23606797749979}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 2.23606797749979}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 49, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}}, "df": 2, "s": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {"controlpi": {"tf": 1.7320508075688772}, "controlpi.run": {"tf": 2}, "controlpi.baseplugin": {"tf": 2.8284271247461903}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 3.872983346207417}, "controlpi.messagebus.Message": {"tf": 2}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 2.449489742783178}, "controlpi.messagebus.Message.setdefault": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.TemplateRegistry": {"tf": 3}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 4}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 2}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 3.4641016151377544}, "controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 2}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 2.6457513110645907}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 41, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}}, "df": 14, "d": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1.7320508075688772}, "controlpi.test": {"tf": 2.449489742783178}, "controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 3.1622776601683795}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 3}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 2.23606797749979}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias": {"tf": 2.6457513110645907}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1.4142135623730951}}, "df": 31, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state": {"tf": 2.8284271247461903}, "controlpi_plugins.state.AndState": {"tf": 3.3166247903554}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndSet": {"tf": 3.605551275463989}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}}, "df": 3}}}}, "y": {"docs": {"controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 8}, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}}, "df": 2}}}}}}, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"controlpi": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}}, "df": 3}}}}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.state.State.process_conf": {"tf": 1}, "controlpi_plugins.state.StateAlias.process_conf": {"tf": 1}, "controlpi_plugins.state.AndState.process_conf": {"tf": 1}, "controlpi_plugins.state.OrState.process_conf": {"tf": 1}, "controlpi_plugins.state.AndSet.process_conf": {"tf": 1}, "controlpi_plugins.state.OrSet.process_conf": {"tf": 1}, "controlpi_plugins.util.Log.process_conf": {"tf": 1}, "controlpi_plugins.util.Init.process_conf": {"tf": 1}, "controlpi_plugins.util.Execute.process_conf": {"tf": 1}, "controlpi_plugins.util.Alias.process_conf": {"tf": 1}, "controlpi_plugins.util.Counter.process_conf": {"tf": 1}, "controlpi_plugins.util.Date.process_conf": {"tf": 1}, "controlpi_plugins.wait.Wait.process_conf": {"tf": 1}, "controlpi_plugins.wait.GenericWait.process_conf": {"tf": 1}, "controlpi_plugins.wait.Timer.process_conf": {"tf": 1}, "controlpi_plugins.wait.Periodic.process_conf": {"tf": 1}}, "df": 26, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 2.449489742783178}, "controlpi.baseplugin.BasePlugin": {"tf": 2.23606797749979}, "controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}}, "df": 11, "i": {"docs": {}, "df": 0, "o": {"docs": {"controlpi.run": {"tf": 2}, "controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 3}, "controlpi.baseplugin.BasePlugin": {"tf": 2}, "controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.send": {"tf": 2}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 2.23606797749979}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 31}}, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.run": {"tf": 1.4142135623730951}, "controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.messagebus": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}}, "df": 7}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 2.23606797749979}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 21, "o": {"docs": {}, "df": 0, "w": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1, "s": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}}, "df": 2}, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 3}}}}}, "s": {"docs": {}, "df": 0, "o": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}}, "df": 11}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}}, "df": 4}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1.7320508075688772}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util": {"tf": 3.1622776601683795}, "controlpi_plugins.util.Alias": {"tf": 5}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}}, "df": 6, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}}, "df": 2}}}}}}, "d": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 3, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 7}}}}}}, "t": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}}, "df": 7}, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.check_value": {"tf": 3}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 2.23606797749979}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}}, "df": 18}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 7}}}}}}}, "g": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.Message": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1}}, "df": 7}}}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}}, "df": 1, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1}}}}}}}}}}, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 7, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}}, "df": 1}}}}}}}}, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "w": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"controlpi": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}}, "df": 9}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 29}}}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}}, "df": 5}, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 4}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"controlpi": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 2.449489742783178}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.__init__": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 3.1622776601683795}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.7320508075688772}, "controlpi_plugins.wait": {"tf": 1}}, "df": 18, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}}, "df": 4}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 1}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1.4142135623730951}}, "df": 1}}}}, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 2}}, "df": 3}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}}, "df": 2, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}, "s": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}}, "df": 1}, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.wait": {"tf": 4.47213595499958}, "controlpi_plugins.wait.Wait": {"tf": 5}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1.4142135623730951}, "controlpi_plugins.wait.GenericWait": {"tf": 4}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 8, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.wait": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "s": {"docs": {"controlpi_plugins.wait": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 1}}, "df": 7, "d": {"docs": {"controlpi": {"tf": 1}, "controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}}, "df": 8}, "f": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.7320508075688772}}, "df": 4}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"controlpi": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}}, "df": 2}}}}}}, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.4142135623730951}}, "df": 3, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}}}}}}}, "i": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}}, "df": 2}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {"controlpi_plugins.util.Counter": {"tf": 2.23606797749979}}, "df": 1}}}}, "p": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 1, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.23606797749979}}, "df": 2}}}}}}, "g": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.__init__": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.4142135623730951}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1.4142135623730951}}, "df": 16}}}}, "t": {"docs": {"controlpi.run": {"tf": 2.449489742783178}, "controlpi.test": {"tf": 2.449489742783178}, "controlpi.baseplugin": {"tf": 4.898979485566356}, "controlpi.baseplugin.BasePlugin": {"tf": 5.291502622129181}, "controlpi.messagebus": {"tf": 4.898979485566356}, "controlpi.messagebus.Message": {"tf": 6}, "controlpi.messagebus.Message.__init__": {"tf": 3.4641016151377544}, "controlpi.messagebus.Message.check_value": {"tf": 6.48074069840786}, "controlpi.messagebus.Message.update": {"tf": 5.196152422706632}, "controlpi.messagebus.Message.setdefault": {"tf": 4.242640687119285}, "controlpi.messagebus.MessageTemplate": {"tf": 3.872983346207417}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 3.4641016151377544}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 4.242640687119285}, "controlpi.messagebus.MessageTemplate.update": {"tf": 5.744562646538029}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 4.58257569495584}, "controlpi.messagebus.MessageTemplate.check": {"tf": 7.937253933193772}, "controlpi.messagebus.TemplateRegistry": {"tf": 7.54983443527075}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 4.242640687119285}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 4.898979485566356}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 3.872983346207417}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 3.4641016151377544}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 6.48074069840786}, "controlpi.messagebus.MessageBus": {"tf": 3.872983346207417}, "controlpi.messagebus.MessageBus.__init__": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus.register": {"tf": 3}, "controlpi.messagebus.MessageBus.unregister": {"tf": 3}, "controlpi.messagebus.MessageBus.run": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageBus.send": {"tf": 3}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 3}, "controlpi.pluginregistry": {"tf": 5.385164807134504}, "controlpi.pluginregistry.PluginRegistry": {"tf": 4.795831523312719}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 4.123105625617661}, "controlpi_plugins.state": {"tf": 3}, "controlpi_plugins.state.State": {"tf": 3}, "controlpi_plugins.state.StateAlias": {"tf": 3}, "controlpi_plugins.state.AndState": {"tf": 3}, "controlpi_plugins.state.OrState": {"tf": 3}, "controlpi_plugins.state.AndSet": {"tf": 3}, "controlpi_plugins.state.OrSet": {"tf": 3}, "controlpi_plugins.util": {"tf": 2.449489742783178}, "controlpi_plugins.util.Log": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Init": {"tf": 3.4641016151377544}, "controlpi_plugins.util.Execute": {"tf": 2.449489742783178}, "controlpi_plugins.util.Alias": {"tf": 4.242640687119285}, "controlpi_plugins.util.Counter": {"tf": 2.449489742783178}, "controlpi_plugins.util.Date": {"tf": 3}, "controlpi_plugins.wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.GenericWait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Timer": {"tf": 2.449489742783178}, "controlpi_plugins.wait.Periodic": {"tf": 2.449489742783178}}, "df": 52}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus.register_schema": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.get_callbacks": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi_plugins.state": {"tf": 2.23606797749979}, "controlpi_plugins.state.State": {"tf": 2.23606797749979}, "controlpi_plugins.state.StateAlias": {"tf": 2.449489742783178}, "controlpi_plugins.state.AndState": {"tf": 2.23606797749979}, "controlpi_plugins.state.OrState": {"tf": 2.23606797749979}, "controlpi_plugins.state.AndSet": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrSet": {"tf": 2}, "controlpi_plugins.util.Counter": {"tf": 3}, "controlpi_plugins.util.Date": {"tf": 2.6457513110645907}}, "df": 16, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 11}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"controlpi.pluginregistry": {"tf": 1.4142135623730951}}, "df": 1, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.wait": {"tf": 3}, "controlpi_plugins.wait.GenericWait": {"tf": 3.3166247903554}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}}, "df": 3}}}}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus.send": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.7320508075688772}}, "df": 2}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 1}}}}}, "g": {"docs": {"controlpi.run": {"tf": 2}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 2.23606797749979}, "controlpi_plugins.util": {"tf": 2.449489742783178}, "controlpi_plugins.util.Log": {"tf": 3.872983346207417}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}}, "df": 6, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageBus": {"tf": 3.7416573867739413}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 3}, "d": {"docs": {"controlpi_plugins.util.Log": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}}, "df": 2}}}, "s": {"docs": {"controlpi_plugins.util": {"tf": 1}}, "df": 1}}, "t": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}, "o": {"docs": {}, "df": 0, "p": {"docs": {"controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 2}, "controlpi_plugins.wait.Periodic.run": {"tf": 1}}, "df": 3}}, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.wait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Wait": {"tf": 2.449489742783178}, "controlpi_plugins.wait.GenericWait": {"tf": 1.7320508075688772}}, "df": 3}}}, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 2}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 15, "s": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 4}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state": {"tf": 2.23606797749979}}, "df": 1}}}, "t": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.4142135623730951}}, "df": 4}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}}, "df": 6}}}, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1}}, "n": {"docs": {"controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}}, "df": 2}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}}, "df": 2}}}}}, "j": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}}, "df": 3, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.register_schema": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 12}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.state": {"tf": 1.7320508075688772}}, "df": 1}}}}, "d": {"docs": {"controlpi_plugins.util.Date": {"tf": 1}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.run": {"tf": 1}}, "df": 1}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.pluginregistry": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 2}}}}}}}}}, "j": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrState": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 1}}, "df": 3}}}}}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}}, "df": 2}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 2}}, "df": 3}}}}}}}}}}, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.pluginregistry": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1}}}}}}}}, "d": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1}}, "e": {"docs": {}, "df": 0, "f": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 2.8284271247461903}, "controlpi.baseplugin.BasePlugin": {"tf": 2.6457513110645907}, "controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}}, "df": 11, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}}, "df": 6, "d": {"docs": {"controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}}, "df": 4}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1.4142135623730951}}, "df": 2}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi_plugins.util.Alias": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1.4142135623730951}}, "df": 2}}}}, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.state": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}}, "df": 2}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.util": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}}, "df": 3}}}}}, "o": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 29}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}}, "df": 2}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 4}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}}, "df": 4}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"controlpi.test": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1.7320508075688772}}, "df": 4}, "e": {"docs": {"controlpi_plugins.util.Date": {"tf": 5.477225575051661}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 2, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}}, "df": 1}}}}, "e": {"docs": {"controlpi_plugins.state": {"tf": 1.7320508075688772}}, "df": 1}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"controlpi.messagebus": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}}, "df": 2}}}}}}}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.run": {"tf": 5.830951894845301}, "controlpi.test": {"tf": 5.830951894845301}, "controlpi.baseplugin": {"tf": 2.8284271247461903}, "controlpi.baseplugin.BasePlugin": {"tf": 2.8284271247461903}, "controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.TemplateRegistry": {"tf": 4.242640687119285}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 2}, "controlpi.pluginregistry.PluginRegistry": {"tf": 2}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.4142135623730951}, "controlpi_plugins.state": {"tf": 11.661903789690601}, "controlpi_plugins.state.State": {"tf": 6.48074069840786}, "controlpi_plugins.state.StateAlias": {"tf": 7.211102550927978}, "controlpi_plugins.state.AndState": {"tf": 8.366600265340756}, "controlpi_plugins.state.OrState": {"tf": 8.366600265340756}, "controlpi_plugins.state.AndSet": {"tf": 9.38083151964686}, "controlpi_plugins.state.OrSet": {"tf": 8.94427190999916}, "controlpi_plugins.util": {"tf": 7.211102550927978}, "controlpi_plugins.util.Log": {"tf": 6.6332495807108}, "controlpi_plugins.util.Init": {"tf": 6.48074069840786}, "controlpi_plugins.util.Execute": {"tf": 6}, "controlpi_plugins.util.Alias": {"tf": 10.770329614269007}, "controlpi_plugins.util.Counter": {"tf": 8.366600265340756}, "controlpi_plugins.util.Date": {"tf": 5.656854249492381}, "controlpi_plugins.wait": {"tf": 6}, "controlpi_plugins.wait.Wait": {"tf": 5.656854249492381}, "controlpi_plugins.wait.GenericWait": {"tf": 5.830951894845301}, "controlpi_plugins.wait.Timer": {"tf": 6.928203230275509}, "controlpi_plugins.wait.Periodic": {"tf": 3.7416573867739413}}, "df": 33}}, "e": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}}, "df": 2}, "s": {"docs": {"controlpi_plugins.util.Counter": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 6, "x": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.run": {"tf": 2.6457513110645907}, "controlpi.test": {"tf": 3.3166247903554}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 3}, "controlpi.messagebus.Message.__init__": {"tf": 2}, "controlpi.messagebus.Message.update": {"tf": 2.449489742783178}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 3.7416573867739413}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 15, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 2}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.4142135623730951}}, "df": 7, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.run": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}}, "df": 7}, "a": {"docs": {}, "df": 0, "l": {"docs": {"controlpi.messagebus.Message": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.test": {"tf": 1.7320508075688772}, "controlpi_plugins.util": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 2.23606797749979}, "controlpi_plugins.util.Execute": {"tf": 4.242640687119285}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}}, "df": 5, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}}, "df": 2}, "s": {"docs": {"controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}}, "df": 2}}}}}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}}, "df": 3}}}}}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry.__init__": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}}, "df": 8}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 2, "t": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 2.23606797749979}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi_plugins.state": {"tf": 3}, "controlpi_plugins.state.State": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1.7320508075688772}, "controlpi_plugins.state.AndState": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrState": {"tf": 2.23606797749979}, "controlpi_plugins.state.AndSet": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrSet": {"tf": 2.23606797749979}, "controlpi_plugins.util": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 2.8284271247461903}, "controlpi_plugins.wait.Wait": {"tf": 2.6457513110645907}, "controlpi_plugins.wait.GenericWait": {"tf": 2.23606797749979}, "controlpi_plugins.wait.Timer": {"tf": 2.8284271247461903}, "controlpi_plugins.wait.Periodic": {"tf": 1.4142135623730951}}, "df": 23, "s": {"docs": {"controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}}, "df": 4}}}, "r": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.test": {"tf": 1}}, "df": 1, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 2}}}}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}}, "df": 4}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}}, "df": 2}, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}}, "df": 2}}}}}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}}, "df": 2}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}}, "df": 10}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}}, "df": 3}}}}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin.ConfException": {"tf": 1}, "controlpi.messagebus.BusException": {"tf": 1}}, "df": 2}}}}}, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}}, "df": 1}}}}}}}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {"controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.State.run": {"tf": 1}, "controlpi_plugins.state.StateAlias.run": {"tf": 1}, "controlpi_plugins.state.AndState.run": {"tf": 1}, "controlpi_plugins.state.OrState.run": {"tf": 1}, "controlpi_plugins.state.AndSet.run": {"tf": 1}, "controlpi_plugins.state.OrSet.run": {"tf": 1}, "controlpi_plugins.util.Log.run": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.run": {"tf": 1}, "controlpi_plugins.util.Alias.run": {"tf": 1}, "controlpi_plugins.util.Counter.run": {"tf": 1}, "controlpi_plugins.util.Date.run": {"tf": 1}, "controlpi_plugins.wait.Wait.run": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait.run": {"tf": 1}, "controlpi_plugins.wait.Timer.run": {"tf": 1}}, "df": 17, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.run": {"tf": 1}, "controlpi.test": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1}, "controlpi_plugins.state.OrSet": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Counter": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 1}}, "df": 29}}}}}}}, "t": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.check_value": {"tf": 2}, "controlpi.messagebus.Message.update": {"tf": 2.449489742783178}, "controlpi.messagebus.Message.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 1}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 1}, "controlpi_plugins.state": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log": {"tf": 2}, "controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 2.23606797749979}}, "df": 17, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.MessageBus.register": {"tf": 1}}, "df": 2}}}}}, "n": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 1, "e": {"docs": {"controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}}, "df": 4}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"controlpi.messagebus.MessageBus.send_nowait": {"tf": 1.7320508075688772}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.baseplugin.BasePlugin": {"tf": 1.7320508075688772}, "controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus": {"tf": 1.7320508075688772}, "controlpi.pluginregistry": {"tf": 2.23606797749979}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.7320508075688772}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1.7320508075688772}, "controlpi_plugins.state": {"tf": 1.4142135623730951}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}}, "df": 13, "s": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}}, "df": 7, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.pluginregistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry.PluginRegistry.__init__": {"tf": 1}}, "df": 3}}}}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}, "w": {"docs": {"controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus.__init__": {"tf": 1}, "controlpi_plugins.state": {"tf": 3.1622776601683795}, "controlpi_plugins.state.State": {"tf": 2.449489742783178}, "controlpi_plugins.state.StateAlias": {"tf": 2.23606797749979}, "controlpi_plugins.state.AndState": {"tf": 2.449489742783178}, "controlpi_plugins.state.OrState": {"tf": 2.449489742783178}, "controlpi_plugins.state.AndSet": {"tf": 3.1622776601683795}, "controlpi_plugins.state.OrSet": {"tf": 2.8284271247461903}, "controlpi_plugins.util.Alias": {"tf": 2.449489742783178}}, "df": 11}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1}}, "df": 1}}}}}}}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"controlpi.messagebus.MessageTemplate": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi_plugins.wait": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}}, "df": 10}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin": {"tf": 1}, "controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.Message": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 8}}, "s": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin.process_conf": {"tf": 1}, "controlpi.messagebus": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi_plugins.util.Log": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.wait.GenericWait": {"tf": 1}}, "df": 11}, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}, "controlpi.baseplugin.BasePlugin.run": {"tf": 1}, "controlpi.pluginregistry.PluginRegistry": {"tf": 1}, "controlpi_plugins.state": {"tf": 1}}, "df": 4}}}, "%": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "%": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.util.Date": {"tf": 1}}, "df": 1}}}}, ":": {"docs": {}, "df": 0, "%": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, ":": {"docs": {}, "df": 0, "%": {"docs": {}, "df": 0, "s": {"docs": {"controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 1}}}}}}}, "v": {"1": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 8.426149773176359}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1.7320508075688772}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 4.123105625617661}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 3}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2.449489742783178}}, "df": 6}, "2": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 6}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 1}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 2.8284271247461903}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2}}, "df": 6}, "docs": {"controlpi.baseplugin": {"tf": 1.4142135623730951}}, "df": 1, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.test": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 3.4641016151377544}, "controlpi.messagebus.Message.update": {"tf": 2}, "controlpi.messagebus.Message.setdefault": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.check": {"tf": 2}, "controlpi_plugins.util.Log": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Init": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Alias": {"tf": 1.7320508075688772}}, "df": 11, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 1}, "controlpi.messagebus.validate": {"tf": 1}, "controlpi.messagebus.MessageTemplate": {"tf": 1}}, "df": 3, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}, "controlpi.baseplugin.BasePlugin": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 3}, "s": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.messagebus.Message.update": {"tf": 1}, "controlpi.messagebus.Message.setdefault": {"tf": 1}, "controlpi.messagebus.MessageTemplate.update": {"tf": 1}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 1}}, "df": 4}}}}}, "u": {"docs": {}, "df": 0, "e": {"docs": {"controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.Message": {"tf": 3.605551275463989}, "controlpi.messagebus.Message.__init__": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.check_value": {"tf": 4.47213595499958}, "controlpi.messagebus.Message.update": {"tf": 3.1622776601683795}, "controlpi.messagebus.Message.setdefault": {"tf": 2.6457513110645907}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 2}, "controlpi.messagebus.MessageTemplate.check": {"tf": 3}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 2}}, "df": 17, "s": {"docs": {"controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 1}}, "df": 8}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"controlpi.baseplugin.BasePlugin": {"tf": 1}}, "df": 1}}}}}}}}}, "k": {"1": {"docs": {"controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.TemplateRegistry": {"tf": 8.54400374531753}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 4.123105625617661}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 3}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2.8284271247461903}, "controlpi.messagebus.MessageBus": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.register": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus.unregister": {"tf": 1}, "controlpi.messagebus.MessageBus.send": {"tf": 2.23606797749979}, "controlpi.messagebus.MessageBus.send_nowait": {"tf": 2.23606797749979}}, "df": 12}, "2": {"docs": {"controlpi.messagebus.TemplateRegistry": {"tf": 8.48528137423857}, "controlpi.messagebus.TemplateRegistry.insert": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.delete": {"tf": 2}, "controlpi.messagebus.TemplateRegistry.check": {"tf": 4.123105625617661}, "controlpi.messagebus.TemplateRegistry.get": {"tf": 3}, "controlpi.messagebus.TemplateRegistry.get_templates": {"tf": 2.8284271247461903}}, "df": 6}, "docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"controlpi.baseplugin": {"tf": 2.6457513110645907}, "controlpi.baseplugin.BasePlugin": {"tf": 3.4641016151377544}, "controlpi.messagebus": {"tf": 2.23606797749979}, "controlpi.messagebus.Message": {"tf": 3.7416573867739413}, "controlpi.messagebus.Message.__init__": {"tf": 1.7320508075688772}, "controlpi.messagebus.Message.check_value": {"tf": 1}, "controlpi.messagebus.Message.update": {"tf": 3.1622776601683795}, "controlpi.messagebus.Message.setdefault": {"tf": 2.449489742783178}, "controlpi.messagebus.MessageTemplate": {"tf": 3.1622776601683795}, "controlpi.messagebus.MessageTemplate.__init__": {"tf": 1.7320508075688772}, "controlpi.messagebus.MessageTemplate.from_message": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.update": {"tf": 5.291502622129181}, "controlpi.messagebus.MessageTemplate.setdefault": {"tf": 3.4641016151377544}, "controlpi.messagebus.MessageTemplate.check": {"tf": 5.744562646538029}, "controlpi.messagebus.TemplateRegistry": {"tf": 1}, "controlpi.messagebus.MessageBus": {"tf": 2}, "controlpi.messagebus.MessageBus.register": {"tf": 1}, "controlpi_plugins.state.State": {"tf": 1}, "controlpi_plugins.state.StateAlias": {"tf": 1}, "controlpi_plugins.state.StateAlias.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndState": {"tf": 1}, "controlpi_plugins.state.AndState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrState": {"tf": 1}, "controlpi_plugins.state.OrState.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet": {"tf": 1.4142135623730951}, "controlpi_plugins.state.OrSet": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Log": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Log.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Init": {"tf": 1.7320508075688772}, "controlpi_plugins.util.Init.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 3}, "controlpi_plugins.util.Counter.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Date": {"tf": 1.4142135623730951}, "controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Wait": {"tf": 1}, "controlpi_plugins.wait.Wait.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Timer": {"tf": 1}, "controlpi_plugins.wait.Timer.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.wait.Periodic": {"tf": 2.23606797749979}, "controlpi_plugins.wait.Periodic.CONF_SCHEMA": {"tf": 1}}, "df": 41, "s": {"docs": {"controlpi.messagebus": {"tf": 2}, "controlpi.messagebus.Message": {"tf": 1.4142135623730951}, "controlpi.messagebus.Message.check_value": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageTemplate.check": {"tf": 1}, "controlpi.messagebus.TemplateRegistry": {"tf": 1.4142135623730951}, "controlpi.messagebus.MessageBus": {"tf": 1}, "controlpi.pluginregistry": {"tf": 1}, "controlpi_plugins.state.State.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.AndSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.state.OrSet.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Execute.CONF_SCHEMA": {"tf": 1}, "controlpi_plugins.util.Alias": {"tf": 2}, "controlpi_plugins.util.Alias.CONF_SCHEMA": {"tf": 1.7320508075688772}, "controlpi_plugins.wait.GenericWait": {"tf": 1}, "controlpi_plugins.wait.GenericWait.CONF_SCHEMA": {"tf": 1}}, "df": 16}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"controlpi.baseplugin": {"tf": 1}}, "df": 1, "s": {"docs": {"controlpi_plugins.state": {"tf": 1}, "controlpi_plugins.util": {"tf": 1}, "controlpi_plugins.util.Execute": {"tf": 1}, "controlpi_plugins.wait": {"tf": 1}}, "df": 4}}}}}, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {"controlpi.messagebus.MessageTemplate.check": {"tf": 1.4142135623730951}}, "df": 1}}}}, "y": {"docs": {"controlpi_plugins.util.Date.CONF_SCHEMA": {"tf": 1}}, "df": 1, "%": {"docs": {}, "df": 0, "m": {"docs": {"controlpi_plugins.util.Date": {"tf": 1}}, "df": 1}}}}}}, "pipeline": ["trimmer"], "_isPrebuiltIndex": true};
+
+ // mirrored in build-search-index.js (part 1)
+ // Also split on html tags. this is a cheap heuristic, but good enough.
+ elasticlunr.tokenizer.setSeperator(/[\s\-.;&_'"=,()]+|<[^>]*>/);
+
+ let searchIndex;
+ if (docs._isPrebuiltIndex) {
+ console.info("using precompiled search index");
+ searchIndex = elasticlunr.Index.load(docs);
+ } else {
+ console.time("building search index");
+ // mirrored in build-search-index.js (part 2)
+ searchIndex = elasticlunr(function () {
+ this.pipeline.remove(elasticlunr.stemmer);
+ this.pipeline.remove(elasticlunr.stopWordFilter);
+ this.addField("qualname");
+ this.addField("fullname");
+ this.addField("annotation");
+ this.addField("default_value");
+ this.addField("signature");
+ this.addField("bases");
+ this.addField("doc");
+ this.setRef("fullname");
+ });
+ for (let doc of docs) {
+ searchIndex.addDoc(doc);
+ }
+ console.timeEnd("building search index");
+ }
+
+ return (term) => searchIndex.search(term, {
+ fields: {
+ qualname: {boost: 4},
+ fullname: {boost: 2},
+ annotation: {boost: 2},
+ default_value: {boost: 2},
+ signature: {boost: 2},
+ bases: {boost: 2},
+ doc: {boost: 1},
+ },
+ expand: true
+ });
+})();
\ No newline at end of file
diff --git a/doc/controlpi/baseplugin.html b/doc/controlpi/baseplugin.html
deleted file mode 100644
index 7f2f42f..0000000
--- a/doc/controlpi/baseplugin.html
+++ /dev/null
@@ -1,621 +0,0 @@
-
-
-
-
-
-
-controlpi.baseplugin API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Module controlpi.baseplugin
-
-
-Define base class for all ControlPi plugins.
-The class BasePlugin provides the abstract base class for concrete plugins
-running on the ControlPi system.
-It has three abstract methods that have to be implemented by all concrete
-plugins:
-- The class property CONF_SCHEMA is the JSON schema of the configuration of
-the plugin. The configuration read from the global configuration file is
-checked against this schema during initialisation.
-- The method process_conf is called at the end of initialisation and is used
-to initialise the plugin. It can be assumed that self.bus is the message
-bus of the system, self.name the instance name, and self.conf the
-configuration already validated against the schema.
-- The run coroutines of all plugins are executed concurrently by the main
-system.
->>> class TestPlugin(BasePlugin):
-... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
-... 'required': ['key']}
-... def process_conf(self):
-... if 'key' in self.conf:
-... print(f"Processing '{self.conf['key']}'.")
-... async def run(self):
-... print("Doing something else.")
-
-Plugins are configured and run based on the information in the global
-configuration. Here, we test this manually:
->>> async def test():
-... p = TestPlugin(MessageBus(), 'Test Instance', {'key': 'Something'})
-... await p.run()
->>> asyncio.run(test())
-Processing 'Something'.
-Doing something else.
-
-Each plugin gets a reference to the system message bus during
-initialisation, which can be accessed as self.bus in the functions of the
-plugin class. This can be used to register and unregister message bus
-clients:
->>> class BusPlugin(BasePlugin):
-... CONF_SCHEMA = True
-... async def receive(self, message):
-... print(f"{self.name} received {message}.")
-... await self.bus.send({'sender': self.name, 'event': 'Receive'})
-... def process_conf(self):
-... self.bus.register(self.name, 'BusPlugin',
-... [{'event': {'type': 'string'}}],
-... [{'target': {'const': self.name}}],
-... self.receive)
-... async def run(self):
-... await self.bus.send({'sender': self.name, 'event': 'Run'})
-
-Again, we run this manually here, but this is done by the main coroutine
-when using the system in production:
->>> async def log(message):
-... print(f"Log: {message}")
->>> async def test_bus_plugin():
-... bus = MessageBus()
-... p = BusPlugin(bus, 'Bus Test', {})
-... bus.register('Test', 'TestPlugin',
-... [{}], [{'sender': {'const': 'Bus Test'}}], log)
-... bus_task = asyncio.create_task(bus.run())
-... asyncio.create_task(p.run())
-... await bus.send({'sender': 'Test', 'target': 'Bus Test', 'key': 'v'})
-... await asyncio.sleep(0)
-... await asyncio.sleep(0)
-... bus_task.cancel()
->>> asyncio.run(test_bus_plugin())
-Bus Test received {'sender': 'Test', 'target': 'Bus Test', 'key': 'v'}.
-Log: {'sender': 'Bus Test', 'event': 'Receive'}
-Log: {'sender': 'Bus Test', 'event': 'Run'}
-
-Often, there will be a one-to-one correspondence between plugin
-instances and message bus clients, a plugin instance will be a message bus
-client. But there are also cases, where one plugin instance might register
-and unregister a lot of message bus clients, maybe even dynamically through
-its lifetime. A plugin for an input/output card might register separate
-clients for each pin of the card, a plugin for some kind of hardware bus
-might register separate clients for all devices connected to the bus, or a
-network socket plugin might register separate clients for all connections
-to the socket (and unregister them when the connection is closed).
-
-
-Expand source code
-
-"""Define base class for all ControlPi plugins.
-
-The class BasePlugin provides the abstract base class for concrete plugins
-running on the ControlPi system.
-
-It has three abstract methods that have to be implemented by all concrete
-plugins:
-- The class property CONF_SCHEMA is the JSON schema of the configuration of
- the plugin. The configuration read from the global configuration file is
- checked against this schema during initialisation.
-- The method process_conf is called at the end of initialisation and is used
- to initialise the plugin. It can be assumed that self.bus is the message
- bus of the system, self.name the instance name, and self.conf the
- configuration already validated against the schema.
-- The run coroutines of all plugins are executed concurrently by the main
- system.
->>> class TestPlugin(BasePlugin):
-... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
-... 'required': ['key']}
-... def process_conf(self):
-... if 'key' in self.conf:
-... print(f"Processing '{self.conf['key']}'.")
-... async def run(self):
-... print("Doing something else.")
-
-Plugins are configured and run based on the information in the global
-configuration. Here, we test this manually:
->>> async def test():
-... p = TestPlugin(MessageBus(), 'Test Instance', {'key': 'Something'})
-... await p.run()
->>> asyncio.run(test())
-Processing 'Something'.
-Doing something else.
-
-Each plugin gets a reference to the system message bus during
-initialisation, which can be accessed as self.bus in the functions of the
-plugin class. This can be used to register and unregister message bus
-clients:
->>> class BusPlugin(BasePlugin):
-... CONF_SCHEMA = True
-... async def receive(self, message):
-... print(f"{self.name} received {message}.")
-... await self.bus.send({'sender': self.name, 'event': 'Receive'})
-... def process_conf(self):
-... self.bus.register(self.name, 'BusPlugin',
-... [{'event': {'type': 'string'}}],
-... [{'target': {'const': self.name}}],
-... self.receive)
-... async def run(self):
-... await self.bus.send({'sender': self.name, 'event': 'Run'})
-
-Again, we run this manually here, but this is done by the main coroutine
-when using the system in production:
->>> async def log(message):
-... print(f"Log: {message}")
->>> async def test_bus_plugin():
-... bus = MessageBus()
-... p = BusPlugin(bus, 'Bus Test', {})
-... bus.register('Test', 'TestPlugin',
-... [{}], [{'sender': {'const': 'Bus Test'}}], log)
-... bus_task = asyncio.create_task(bus.run())
-... asyncio.create_task(p.run())
-... await bus.send({'sender': 'Test', 'target': 'Bus Test', 'key': 'v'})
-... await asyncio.sleep(0)
-... await asyncio.sleep(0)
-... bus_task.cancel()
->>> asyncio.run(test_bus_plugin())
-Bus Test received {'sender': 'Test', 'target': 'Bus Test', 'key': 'v'}.
-Log: {'sender': 'Bus Test', 'event': 'Receive'}
-Log: {'sender': 'Bus Test', 'event': 'Run'}
-
-Often, there will be a one-to-one correspondence between plugin
-instances and message bus clients, a plugin instance will be a message bus
-client. But there are also cases, where one plugin instance might register
-and unregister a lot of message bus clients, maybe even dynamically through
-its lifetime. A plugin for an input/output card might register separate
-clients for each pin of the card, a plugin for some kind of hardware bus
-might register separate clients for all devices connected to the bus, or a
-network socket plugin might register separate clients for all connections
-to the socket (and unregister them when the connection is closed).
-"""
-__pdoc__ = {'BasePlugin.CONF_SCHEMA': False}
-
-from abc import ABC, abstractmethod
-import asyncio
-import jsonschema # type: ignore
-
-from controlpi.messagebus import MessageBus
-
-from typing import Union, Dict, List, Any
-JSONSchema = Union[bool, Dict[str, Union[None, str, int, float, bool,
- Dict[str, Any], List[Any]]]]
-# Could be more specific.
-PluginConf = Dict[str, Any]
-# Could be more specific.
-
-
-class ConfException(Exception):
- """Raise for errors in plugin configurations."""
-
-
-class BasePlugin(ABC):
- """Base class for all ControlPi plugins.
-
- >>> class TestPlugin(BasePlugin):
- ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
- ... 'required': ['key']}
- ... def process_conf(self):
- ... if 'key' in self.conf:
- ... print(f"Processing '{self.conf['key']}'.")
- ... async def run(self):
- ... print("Doing something else.")
-
- Initialisation sets the instance variables bus to the given message bus,
- name to the given name, and conf to the given configuration:
- >>> class TestPlugin(BasePlugin):
- ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
- ... 'required': ['key']}
- ... def process_conf(self):
- ... if 'key' in self.conf:
- ... print(f"Processing '{self.conf['key']}'.")
- ... async def run(self):
- ... print("Doing something else.")
- >>> async def test():
- ... p = TestPlugin(MessageBus(), 'Test Instance',
- ... {'key': 'Something'})
- ... print(p.bus)
- ... print(p.name)
- ... print(p.conf)
- >>> asyncio.run(test()) # doctest: +ELLIPSIS
- Processing 'Something'.
- <controlpi.messagebus.MessageBus object at 0x...>
- Test Instance
- {'key': 'Something'}
-
- It also validates the configuration against the schema in CONF_SCHEMA
- and raises ConfException if is not validated.
- >>> async def test():
- ... p = TestPlugin(MessageBus(), 'Test Instance',
- ... {'key': 42})
- >>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- baseplugin.ConfException: Configuration for 'Test Instance'
- is not valid.
- >>> async def test():
- ... p = TestPlugin(MessageBus(), 'Test Instance',
- ... {'key 2': 'Something'})
- >>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- baseplugin.ConfException: Configuration for 'Test Instance'
- is not valid.
-
- Finally, it calls process_conf, which is the function that should be
- overridden by concrete plugins.
- """
-
- @property
- @classmethod
- @abstractmethod
- def CONF_SCHEMA(cls) -> JSONSchema:
- """JSON schema for configuration of plugin.
-
- Given configurations are validated against this schema in __init__.
- process_conf and run can assume a valid configuration in self.conf.
- """
- raise NotImplementedError
-
- def __init__(self, bus: MessageBus, name: str, conf: PluginConf) -> None:
- assert isinstance(bus, MessageBus)
- self.bus = bus
- assert isinstance(name, str)
- self.name = name
- jsonschema.Draft7Validator.check_schema(type(self).CONF_SCHEMA)
- validator = jsonschema.Draft7Validator(type(self).CONF_SCHEMA)
- assert isinstance(conf, dict)
- valid = True
- for error in validator.iter_errors(conf):
- print(error)
- valid = False
- if not valid:
- raise ConfException(f"Configuration for '{self.name}'"
- " is not valid.")
- self.conf = conf
- self.process_conf()
-
- @abstractmethod
- def process_conf(self) -> None:
- """Process the configuration.
-
- Abstract method has to be overridden by concrete plugins.
- process_conf is called at the end of initialisation after the bus
- and the configuration are available as self.bus and self.conf, but
- before any of the run coroutines are executed.
- """
- raise NotImplementedError
-
- @abstractmethod
- async def run(self) -> None:
- """Run the plugin.
-
- The coroutine is run concurrently with the message bus and all
- other plugins. Initial messages and other tasks can be done here.
- It is also okay to run a plugin-specific infinite loop concurrently
- with the rest of the system.
- """
- raise NotImplementedError
-
-
-
-
-
-
-
-
-
-class ConfException
-( *args, **kwargs)
-
-
-Raise for errors in plugin configurations.
-
-
-Expand source code
-
-class ConfException(Exception):
- """Raise for errors in plugin configurations."""
-
-Ancestors
-
-builtins.Exception
-builtins.BaseException
-
-
-
-class BasePlugin
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Base class for all ControlPi plugins.
-
>>> class TestPlugin(BasePlugin):
-... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
-... 'required': ['key']}
-... def process_conf(self):
-... if 'key' in self.conf:
-... print(f"Processing '{self.conf['key']}'.")
-... async def run(self):
-... print("Doing something else.")
-
-
Initialisation sets the instance variables bus to the given message bus,
-name to the given name, and conf to the given configuration:
-
>>> class TestPlugin(BasePlugin):
-... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
-... 'required': ['key']}
-... def process_conf(self):
-... if 'key' in self.conf:
-... print(f"Processing '{self.conf['key']}'.")
-... async def run(self):
-... print("Doing something else.")
->>> async def test():
-... p = TestPlugin(MessageBus(), 'Test Instance',
-... {'key': 'Something'})
-... print(p.bus)
-... print(p.name)
-... print(p.conf)
->>> asyncio.run(test()) # doctest: +ELLIPSIS
-Processing 'Something'.
-<controlpi.messagebus.MessageBus object at 0x...>
-Test Instance
-{'key': 'Something'}
-
-
It also validates the configuration against the schema in CONF_SCHEMA
-and raises ConfException if is not validated.
-
>>> async def test():
-... p = TestPlugin(MessageBus(), 'Test Instance',
-... {'key': 42})
->>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
-Traceback (most recent call last):
- ...
-baseplugin.ConfException: Configuration for 'Test Instance'
-is not valid.
->>> async def test():
-... p = TestPlugin(MessageBus(), 'Test Instance',
-... {'key 2': 'Something'})
->>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
-Traceback (most recent call last):
- ...
-baseplugin.ConfException: Configuration for 'Test Instance'
-is not valid.
-
-
Finally, it calls process_conf, which is the function that should be
-overridden by concrete plugins.
-
-
-Expand source code
-
-class BasePlugin(ABC):
- """Base class for all ControlPi plugins.
-
- >>> class TestPlugin(BasePlugin):
- ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
- ... 'required': ['key']}
- ... def process_conf(self):
- ... if 'key' in self.conf:
- ... print(f"Processing '{self.conf['key']}'.")
- ... async def run(self):
- ... print("Doing something else.")
-
- Initialisation sets the instance variables bus to the given message bus,
- name to the given name, and conf to the given configuration:
- >>> class TestPlugin(BasePlugin):
- ... CONF_SCHEMA = {'properties': {'key': {'type': 'string'}},
- ... 'required': ['key']}
- ... def process_conf(self):
- ... if 'key' in self.conf:
- ... print(f"Processing '{self.conf['key']}'.")
- ... async def run(self):
- ... print("Doing something else.")
- >>> async def test():
- ... p = TestPlugin(MessageBus(), 'Test Instance',
- ... {'key': 'Something'})
- ... print(p.bus)
- ... print(p.name)
- ... print(p.conf)
- >>> asyncio.run(test()) # doctest: +ELLIPSIS
- Processing 'Something'.
- <controlpi.messagebus.MessageBus object at 0x...>
- Test Instance
- {'key': 'Something'}
-
- It also validates the configuration against the schema in CONF_SCHEMA
- and raises ConfException if is not validated.
- >>> async def test():
- ... p = TestPlugin(MessageBus(), 'Test Instance',
- ... {'key': 42})
- >>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- baseplugin.ConfException: Configuration for 'Test Instance'
- is not valid.
- >>> async def test():
- ... p = TestPlugin(MessageBus(), 'Test Instance',
- ... {'key 2': 'Something'})
- >>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- baseplugin.ConfException: Configuration for 'Test Instance'
- is not valid.
-
- Finally, it calls process_conf, which is the function that should be
- overridden by concrete plugins.
- """
-
- @property
- @classmethod
- @abstractmethod
- def CONF_SCHEMA(cls) -> JSONSchema:
- """JSON schema for configuration of plugin.
-
- Given configurations are validated against this schema in __init__.
- process_conf and run can assume a valid configuration in self.conf.
- """
- raise NotImplementedError
-
- def __init__(self, bus: MessageBus, name: str, conf: PluginConf) -> None:
- assert isinstance(bus, MessageBus)
- self.bus = bus
- assert isinstance(name, str)
- self.name = name
- jsonschema.Draft7Validator.check_schema(type(self).CONF_SCHEMA)
- validator = jsonschema.Draft7Validator(type(self).CONF_SCHEMA)
- assert isinstance(conf, dict)
- valid = True
- for error in validator.iter_errors(conf):
- print(error)
- valid = False
- if not valid:
- raise ConfException(f"Configuration for '{self.name}'"
- " is not valid.")
- self.conf = conf
- self.process_conf()
-
- @abstractmethod
- def process_conf(self) -> None:
- """Process the configuration.
-
- Abstract method has to be overridden by concrete plugins.
- process_conf is called at the end of initialisation after the bus
- and the configuration are available as self.bus and self.conf, but
- before any of the run coroutines are executed.
- """
- raise NotImplementedError
-
- @abstractmethod
- async def run(self) -> None:
- """Run the plugin.
-
- The coroutine is run concurrently with the message bus and all
- other plugins. Initial messages and other tasks can be done here.
- It is also okay to run a plugin-specific infinite loop concurrently
- with the rest of the system.
- """
- raise NotImplementedError
-
-Ancestors
-
-Subclasses
-
-Methods
-
-
-def process_conf (self) â>Â NoneType
-
-
-Process the configuration.
-
Abstract method has to be overridden by concrete plugins.
-process_conf is called at the end of initialisation after the bus
-and the configuration are available as self.bus and self.conf, but
-before any of the run coroutines are executed.
-
-
-Expand source code
-
-@abstractmethod
-def process_conf(self) -> None:
- """Process the configuration.
-
- Abstract method has to be overridden by concrete plugins.
- process_conf is called at the end of initialisation after the bus
- and the configuration are available as self.bus and self.conf, but
- before any of the run coroutines are executed.
- """
- raise NotImplementedError
-
-
-
-async def run (self) â>Â NoneType
-
-
-Run the plugin.
-
The coroutine is run concurrently with the message bus and all
-other plugins. Initial messages and other tasks can be done here.
-It is also okay to run a plugin-specific infinite loop concurrently
-with the rest of the system.
-
-
-Expand source code
-
-@abstractmethod
-async def run(self) -> None:
- """Run the plugin.
-
- The coroutine is run concurrently with the message bus and all
- other plugins. Initial messages and other tasks can be done here.
- It is also okay to run a plugin-specific infinite loop concurrently
- with the rest of the system.
- """
- raise NotImplementedError
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/controlpi/index.html b/doc/controlpi/index.html
deleted file mode 100644
index d42fb6c..0000000
--- a/doc/controlpi/index.html
+++ /dev/null
@@ -1,476 +0,0 @@
-
-
-
-
-
-
-controlpi API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Provide the infrastructure for the ControlPi system.
-The infrastructure consists of the message bus from module messagebus, the
-plugin registry from module pluginregistry and the abstract base plugin from
-module baseplugin.
-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.
-
-
-Expand source code
-
-"""Provide the infrastructure for the ControlPi system.
-
-The infrastructure consists of the message bus from module messagebus, the
-plugin registry from module pluginregistry and the abstract base plugin from
-module baseplugin.
-
-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 controlpi.messagebus import MessageBus, Message, MessageTemplate
-from controlpi.pluginregistry import PluginRegistry
-from controlpi.baseplugin import BasePlugin, PluginConf, ConfException
-
-from typing import Dict, List, Coroutine, Any
-
-
-CONF_SCHEMA = {'type': 'object',
- 'patternProperties': {'.*': {'type': 'object'}}}
-
-
-def _process_conf(message_bus: MessageBus,
- conf: Dict[str, PluginConf]) -> List[Coroutine]:
- jsonschema.Draft7Validator.check_schema(CONF_SCHEMA)
- validator = jsonschema.Draft7Validator(CONF_SCHEMA)
- valid = True
- for error in validator.iter_errors(conf):
- print(error)
- valid = False
- if not valid:
- return []
- plugins = PluginRegistry('controlpi_plugins', BasePlugin)
- coroutines = [message_bus.run()]
- for instance_name in conf:
- instance_conf = conf[instance_name]
- if 'plugin' not in instance_conf:
- print("No plugin implementation specified for instance"
- f" '{instance_name}'.")
- continue
- plugin_name = instance_conf['plugin']
- if plugin_name not in plugins:
- print(f"No implementation found for plugin '{plugin_name}'"
- f" (specified for instance '{instance_name}').")
- continue
- plugin = plugins[plugin_name]
- try:
- instance = plugin(message_bus, instance_name, instance_conf)
- coroutines.append(instance.run())
- except ConfException as e:
- print(e)
- continue
- return coroutines
-
-
-async def run(conf: Dict[str, PluginConf]) -> None:
- """Run the ControlPi system based on a configuration.
-
- Setup message bus, process given configuration, and run message bus and
- plugins concurrently and indefinitely.
-
- 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)
- try:
- await asyncio.gather(*coroutines)
- except asyncio.exceptions.CancelledError:
- pass
-
-
-async def test(conf: Dict[str, PluginConf],
- messages: List[Dict[str, Any]],
- wait: float = 0.0) -> None:
- """Test configuration of ControlPi system.
-
- Setup message bus, process given configuration, run message bus and
- 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,
- ... "content": "Test Message"},
- ... {"id": 42.42,
- ... "content": "Second Message"}]}},
- ... [{"target": "Example Init",
- ... "command": "execute"}])) # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Example Init', 'plugin': 'Init',
- 'sends': [{'id': {'const': 42},
- 'content': {'const': 'Test Message'}},
- {'id': {'const': 42.42},
- 'content': {'const': 'Second Message'}}],
- 'receives': [{'target': {'const': 'Example Init'},
- 'command': {'const': '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 indefinitely 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': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['messages']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Init'}
- Configuration for 'Example Init' is not valid.
- """
- message_bus = MessageBus()
-
- async def log(message):
- if ('sender' in message and message['sender'] == '' and
- 'event' in message and message['event'] == 'registered' and
- 'client' in message and message['client'] == 'test()'):
- # Do not log own registration of 'test()':
- return
- print(f"test(): {message}")
- message_bus.register('test()', 'Test',
- [MessageTemplate()], [MessageTemplate()], log)
-
- coroutines = _process_conf(message_bus, conf)
- for coroutine in coroutines:
- asyncio.create_task(coroutine)
- # Give the created task opportunity to run:
- await asyncio.sleep(0)
- for message in messages:
- await message_bus.send(Message('test()', message))
- # Give immediate reactions to messages opportunity to happen:
- await asyncio.sleep(0)
- await asyncio.sleep(wait)
- await message_bus._queue.join()
-
-
-
-
-
-
-
-
-async def run (conf: Dict[str, Dict[str, Any]]) â> NoneType
-
-
-Run the ControlPi system based on a configuration.
-
Setup message bus, process given configuration, and run message bus and
-plugins concurrently and indefinitely.
-
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'}
-
-
-
-Expand source code
-
-async def run(conf: Dict[str, PluginConf]) -> None:
- """Run the ControlPi system based on a configuration.
-
- Setup message bus, process given configuration, and run message bus and
- plugins concurrently and indefinitely.
-
- 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)
- try:
- await asyncio.gather(*coroutines)
- except asyncio.exceptions.CancelledError:
- pass
-
-
-
-async def test (conf: Dict[str, Dict[str, Any]], messages: List[Dict[str, Any]], wait: float = 0.0) â> NoneType
-
-
-Test configuration of ControlPi system.
-
Setup message bus, process given configuration, run message bus and
-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,
-... "content": "Test Message"},
-... {"id": 42.42,
-... "content": "Second Message"}]}},
-... [{"target": "Example Init",
-... "command": "execute"}])) # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Example Init', 'plugin': 'Init',
- 'sends': [{'id': {'const': 42},
- 'content': {'const': 'Test Message'}},
- {'id': {'const': 42.42},
- 'content': {'const': 'Second Message'}}],
- 'receives': [{'target': {'const': 'Example Init'},
- 'command': {'const': '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 indefinitely 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': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['messages']}
-<BLANKLINE>
-On instance:
- {'plugin': 'Init'}
-Configuration for 'Example Init' is not valid.
-
-
-
-Expand source code
-
-async def test(conf: Dict[str, PluginConf],
- messages: List[Dict[str, Any]],
- wait: float = 0.0) -> None:
- """Test configuration of ControlPi system.
-
- Setup message bus, process given configuration, run message bus and
- 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,
- ... "content": "Test Message"},
- ... {"id": 42.42,
- ... "content": "Second Message"}]}},
- ... [{"target": "Example Init",
- ... "command": "execute"}])) # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Example Init', 'plugin': 'Init',
- 'sends': [{'id': {'const': 42},
- 'content': {'const': 'Test Message'}},
- {'id': {'const': 42.42},
- 'content': {'const': 'Second Message'}}],
- 'receives': [{'target': {'const': 'Example Init'},
- 'command': {'const': '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 indefinitely 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': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['messages']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Init'}
- Configuration for 'Example Init' is not valid.
- """
- message_bus = MessageBus()
-
- async def log(message):
- if ('sender' in message and message['sender'] == '' and
- 'event' in message and message['event'] == 'registered' and
- 'client' in message and message['client'] == 'test()'):
- # Do not log own registration of 'test()':
- return
- print(f"test(): {message}")
- message_bus.register('test()', 'Test',
- [MessageTemplate()], [MessageTemplate()], log)
-
- coroutines = _process_conf(message_bus, conf)
- for coroutine in coroutines:
- asyncio.create_task(coroutine)
- # Give the created task opportunity to run:
- await asyncio.sleep(0)
- for message in messages:
- await message_bus.send(Message('test()', message))
- # Give immediate reactions to messages opportunity to happen:
- await asyncio.sleep(0)
- await asyncio.sleep(wait)
- await message_bus._queue.join()
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/controlpi/messagebus.html b/doc/controlpi/messagebus.html
deleted file mode 100644
index 46bea91..0000000
--- a/doc/controlpi/messagebus.html
+++ /dev/null
@@ -1,4244 +0,0 @@
-
-
-
-
-
-
-controlpi.messagebus API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Module controlpi.messagebus
-
-
-Provide an asynchronous message bus.
-A message is a dictionary with string keys and string, integer, float,
-Boolean, dictionary, or list values, where the inner dictionaries again
-have string keys and these values and the inner lists also have elements of
-these types. All messages have a special key 'sender' with the name of the
-sending client as string value, which is set by the constructor:
->>> m = Message('Example sender', {'key 1': 'value 1'})
->>> m['key 2'] = 'value 2'
->>> print(m)
-{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
-
-A message template is a mapping from string keys to JSON schemas as values.
-A message template matches a message if all keys of the template are
-contained in the message and the values in the message validate against the
-respective schemas. An empty mapping therefore matches all messages.
-The bus executes asynchronous callbacks for all messages to be received by
-a client. We use a simple callback printing the message in all examples:
->>> def callback_for_receiver(receiver):
-... async def callback(message):
-... print(f"{receiver}: {message}")
-... return callback
-
-Clients can be registered at the bus with a name, lists of message templates
-they want to use for sending and receiving and a callback function for
-receiving. An empty list of templates means that the client does not want to
-send or receive any messages, respectively. A list with an empty template
-means that it wants to send arbitrary or receive all messages, respectively:
->>> async def setup(bus):
-... bus.register('Logger', 'Test Plugin',
-... [],
-... [{}],
-... callback_for_receiver('Logger'))
-... bus.register('Client 1', 'Test Plugin',
-... [{'k1': {'type': 'string'}}],
-... [{'target': {'const': 'Client 1'}}],
-... callback_for_receiver('Client 1'))
-
-While most clients should always use their own name for sending, this is not
-enforced and debugging or management clients could send messages on behalf
-of arbitrary client names.
-The name of a client has to be unique and is not allowed to be empty
-(otherwise registration fails).
-The empty name is used to refer to the bus itself. The bus sends messages
-for registrations and deregistrations of clients containing their complete
-interface of send and receive templates. This can be used to allow dynamic
-(debug) clients to deal with arbitrary configurations of clients. The bus
-also reacts to 'get clients' command messages by sending the complete
-information of all currently registered clients.
-Clients can send to the bus with the send function. Each message has to
-declare a sender. The send templates of that sender are checked for a
-template matching the message:
->>> async def send(bus):
-... print("Sending messages.")
-... await bus.send({'sender': 'Client 1', 'k1': 'Test'})
-... await bus.send({'sender': '', 'target': 'Client 1'})
-
-The run function executes the message bus forever. If we want to stop it, we
-have to explicitly cancel the task:
->>> async def main():
-... bus = MessageBus()
-... await setup(bus)
-... bus_task = asyncio.create_task(bus.run())
-... await send(bus)
-... await asyncio.sleep(0)
-... bus_task.cancel()
->>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
-Sending messages.
-Logger: {'sender': '', 'event': 'registered',
- 'client': 'Logger', 'plugin': 'Test Plugin',
- 'sends': [], 'receives': [{}]}
-Logger: {'sender': '', 'event': 'registered',
- 'client': 'Client 1', 'plugin': 'Test Plugin',
- 'sends': [{'k1': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Client 1'}}]}
-Logger: {'sender': 'Client 1', 'k1': 'Test'}
-Logger: {'sender': '', 'target': 'Client 1'}
-Client 1: {'sender': '', 'target': 'Client 1'}
-
-
-
-Expand source code
-
-"""Provide an asynchronous message bus.
-
-A message is a dictionary with string keys and string, integer, float,
-Boolean, dictionary, or list values, where the inner dictionaries again
-have string keys and these values and the inner lists also have elements of
-these types. All messages have a special key 'sender' with the name of the
-sending client as string value, which is set by the constructor:
->>> m = Message('Example sender', {'key 1': 'value 1'})
->>> m['key 2'] = 'value 2'
->>> print(m)
-{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
-
-A message template is a mapping from string keys to JSON schemas as values.
-A message template matches a message if all keys of the template are
-contained in the message and the values in the message validate against the
-respective schemas. An empty mapping therefore matches all messages.
-
-The bus executes asynchronous callbacks for all messages to be received by
-a client. We use a simple callback printing the message in all examples:
->>> def callback_for_receiver(receiver):
-... async def callback(message):
-... print(f"{receiver}: {message}")
-... return callback
-
-Clients can be registered at the bus with a name, lists of message templates
-they want to use for sending and receiving and a callback function for
-receiving. An empty list of templates means that the client does not want to
-send or receive any messages, respectively. A list with an empty template
-means that it wants to send arbitrary or receive all messages, respectively:
->>> async def setup(bus):
-... bus.register('Logger', 'Test Plugin',
-... [],
-... [{}],
-... callback_for_receiver('Logger'))
-... bus.register('Client 1', 'Test Plugin',
-... [{'k1': {'type': 'string'}}],
-... [{'target': {'const': 'Client 1'}}],
-... callback_for_receiver('Client 1'))
-
-While most clients should always use their own name for sending, this is not
-enforced and debugging or management clients could send messages on behalf
-of arbitrary client names.
-
-The name of a client has to be unique and is not allowed to be empty
-(otherwise registration fails).
-
-The empty name is used to refer to the bus itself. The bus sends messages
-for registrations and deregistrations of clients containing their complete
-interface of send and receive templates. This can be used to allow dynamic
-(debug) clients to deal with arbitrary configurations of clients. The bus
-also reacts to 'get clients' command messages by sending the complete
-information of all currently registered clients.
-
-Clients can send to the bus with the send function. Each message has to
-declare a sender. The send templates of that sender are checked for a
-template matching the message:
->>> async def send(bus):
-... print("Sending messages.")
-... await bus.send({'sender': 'Client 1', 'k1': 'Test'})
-... await bus.send({'sender': '', 'target': 'Client 1'})
-
-The run function executes the message bus forever. If we want to stop it, we
-have to explicitly cancel the task:
->>> async def main():
-... bus = MessageBus()
-... await setup(bus)
-... bus_task = asyncio.create_task(bus.run())
-... await send(bus)
-... await asyncio.sleep(0)
-... bus_task.cancel()
->>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
-Sending messages.
-Logger: {'sender': '', 'event': 'registered',
- 'client': 'Logger', 'plugin': 'Test Plugin',
- 'sends': [], 'receives': [{}]}
-Logger: {'sender': '', 'event': 'registered',
- 'client': 'Client 1', 'plugin': 'Test Plugin',
- 'sends': [{'k1': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Client 1'}}]}
-Logger: {'sender': 'Client 1', 'k1': 'Test'}
-Logger: {'sender': '', 'target': 'Client 1'}
-Client 1: {'sender': '', 'target': 'Client 1'}
-"""
-import asyncio
-import json
-import jsonschema # type: ignore
-
-from typing import Union, Dict, List, Any, Iterable, Callable, Coroutine
-MessageValue = Union[None, str, int, float, bool, Dict[str, Any], List[Any]]
-# Should really be:
-# MessageValue = Union[None, str, int, float, bool,
-# Dict[str, 'MessageValue'], List['MessageValue']]
-# But mypy does not support recursion by now:
-# https://github.com/python/mypy/issues/731
-JSONSchema = Union[bool, Dict[str, MessageValue]]
-# Could be even more specific.
-MessageCallback = Callable[['Message'], Coroutine[Any, Any, None]]
-
-
-class Message(Dict[str, MessageValue]):
- """Define arbitrary message.
-
- Messages are dictionaries with string keys and values that are strings,
- integers, floats, Booleans, dictionaries that recursively have string
- keys and values of any of these types, or lists with elements that have
- any of these types. These constraints are checked when setting key-value
- pairs of the message.
-
- A message has to have a sender, which is set by the constructor:
- >>> m = Message('Example sender')
- >>> print(m)
- {'sender': 'Example sender'}
-
- A dictionary can be given to the constructor:
- >>> m = Message('Example sender', {'key 1': 'value 1', 'key 2': 'value 2'})
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
-
- Or the message can be modified after construction:
- >>> m = Message('Example sender', {'key 1': 'value 1'})
- >>> m['key 2'] = 'value 2'
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
- """
-
- def __init__(self, sender: str,
- init: Dict[str, MessageValue] = None) -> None:
- """Initialise message.
-
- Message is initialised with given sender and possibly given
- key-value pairs:
- >>> m = Message('Example sender')
- >>> print(m)
- {'sender': 'Example sender'}
- >>> m = Message('Example sender', {'key 1': 'value 1'})
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1'}
-
- The sender can be overwritten by the key-value pairs:
- >>> m = Message('Example sender', {'sender': 'Another sender'})
- >>> print(m)
- {'sender': 'Another sender'}
- """
- if not isinstance(sender, str):
- raise TypeError(f"'{sender}' is not a valid sender name"
- " (not a string).")
- self['sender'] = sender
- if init is not None:
- self.update(init)
-
- @staticmethod
- def check_value(value: MessageValue) -> bool:
- """Check recursively if a given value is valid.
-
- None, strings, integers, floats and Booleans are valid:
- >>> Message.check_value(None)
- True
- >>> Message.check_value('Spam')
- True
- >>> Message.check_value(42)
- True
- >>> Message.check_value(42.42)
- True
- >>> Message.check_value(False)
- True
-
- Other basic types are not valid:
- >>> Message.check_value(b'bytes')
- False
- >>> Message.check_value(1j)
- False
-
- Dictionaries with string keys and recursively valid values are valid:
- >>> Message.check_value({'str value': 'Spam', 'int value': 42,
- ... 'float value': 42.42, 'bool value': False})
- True
-
- Empty dictionaries are valid:
- >>> Message.check_value({})
- True
-
- Dictionaries with other keys are not valid:
- >>> Message.check_value({42: 'int key'})
- False
-
- Dictionaries with invalid values are not valid:
- >>> Message.check_value({'complex value': 1j})
- False
-
- Lists with valid elements are valid:
- >>> Message.check_value(['Spam', 42, 42.42, False])
- True
-
- Empty lists are valid:
- >>> Message.check_value([])
- True
-
- Lists with invalid elements are not valid:
- >>> Message.check_value([1j])
- False
- """
- if value is None:
- return True
- elif (isinstance(value, str) or isinstance(value, int) or
- isinstance(value, float) or isinstance(value, bool)):
- return True
- elif isinstance(value, dict):
- for key in value:
- if not isinstance(key, str):
- return False
- if not Message.check_value(value[key]):
- return False
- return True
- elif isinstance(value, list):
- for element in value:
- if not Message.check_value(element):
- return False
- return True
- return False
-
- def __setitem__(self, key: str, value: MessageValue) -> None:
- """Check key and value before putting pair into dict.
-
- >>> m = Message('Example sender')
- >>> m['key'] = 'value'
- >>> m['dict'] = {'k1': 'v1', 'k2': 2}
- >>> print(m) # doctest: +NORMALIZE_WHITESPACE
- {'sender': 'Example sender', 'key': 'value',
- 'dict': {'k1': 'v1', 'k2': 2}}
- >>> m = Message('Example sender')
- >>> m[42] = 'int key'
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m['complex value'] = 1j
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
- """
- if not isinstance(key, str):
- raise TypeError(f"'{key}' is not a valid key in Message"
- " (not a string).")
- if not self.check_value(value):
- raise TypeError(f"'{value}' is not a valid value in Message.")
- super().__setitem__(key, value)
-
- def update(self, *args, **kwargs) -> None:
- """Override update to use validity checks.
-
- >>> m = Message('Example sender')
- >>> m.update({'key 1': 'value 1', 'key 2': 'value 2'})
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
- >>> m.update({42: 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m.update({'complex value': 1j})
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
-
- This is also used in __init__:
- >>> m = Message('Example sender', {'key': 'value'})
- >>> print(m)
- {'sender': 'Example sender', 'key': 'value'}
- >>> m = Message('Example sender', {42: 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m = Message('Example sender', {'complex value': 1j})
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
- """
- if args:
- if len(args) > 1:
- raise TypeError("update expected at most 1 argument,"
- f" got {len(args)}")
- other = dict(args[0])
- for key in other:
- self[key] = other[key]
- for key in kwargs:
- self[key] = kwargs[key]
-
- def setdefault(self, key: str, value: MessageValue = None) -> MessageValue:
- """Override setdefault to use validity checks.
-
- >>> m = Message('Example sender')
- >>> m.setdefault('key', 'value 1')
- 'value 1'
- >>> m.setdefault('key', 'value 2')
- 'value 1'
- >>> m.setdefault(42, 'int key')
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m.setdefault('complex value', 1j)
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
-
- But __setitem__ is not called if the key is already present:
- >>> m.setdefault('key', 1j)
- 'value 1'
- """
- if key not in self:
- self[key] = value
- return self[key]
-
-
-class MessageTemplate(Dict[str, JSONSchema]):
- """Define a message template.
-
- A message template is a mapping from string keys to JSON schemas as
- values:
- >>> t = MessageTemplate({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'}})
- >>> t['key 3'] = {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}}
-
- A message template matches a message if all keys of the template are
- contained in the message and the values in the message validate against
- the respective schemas:
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 1': 42, 'key 2': None}}))
- True
-
- An empty mapping therefore matches all messages:
- >>> t = MessageTemplate()
- >>> t.check(Message('Example Sender', {'arbitrary': 'content'}))
- True
- """
-
- def __init__(self, init: Dict[str, JSONSchema] = None) -> None:
- """Initialise message.
-
- Template is initialised empty or with given key-value pairs:
- >>> t = MessageTemplate()
- >>> print(t)
- {}
- >>> t = MessageTemplate({'key': {'const': 'value'}})
- >>> print(t)
- {'key': {'const': 'value'}}
- """
- if init is not None:
- self.update(init)
-
- @staticmethod
- def from_message(message: Message) -> 'MessageTemplate':
- """Create template from message.
-
- Template witch constant schemas is created from message:
- >>> m = Message('Example Sender', {'key': 'value'})
- >>> t = MessageTemplate.from_message(m)
- >>> print(t)
- {'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}}
- >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
- ... 'list': [None, True, 'string']})
- >>> t = MessageTemplate.from_message(m)
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'sender': {'const': 'Example Sender'},
- 'dict': {'type': 'object',
- 'properties': {'int': {'const': 42},
- 'float': {'const': 42.42}}},
- 'list': {'type': 'array',
- 'items': [{'const': None},
- {'const': True},
- {'const': 'string'}]}}
-
- This is especially useful for clients that send certain fully
- predefined messages, where the message is given in the configuration
- and the template for the registration can be constructed by this
- method.
- """
- def schema_from_value(value: MessageValue) -> JSONSchema:
- schema: JSONSchema = False
- if value is None:
- schema = {'const': None}
- elif (isinstance(value, str) or isinstance(value, int) or
- isinstance(value, float) or isinstance(value, bool)):
- schema = {'const': value}
- elif isinstance(value, dict):
- properties = {}
- for inner_key in value:
- inner_value: Message = value[inner_key]
- properties[inner_key] = schema_from_value(inner_value)
- schema = {'type': 'object',
- 'properties': properties}
- elif isinstance(value, list):
- schema = {'type': 'array',
- 'items': [schema_from_value(element)
- for element in value]}
- return schema
- template = MessageTemplate()
- for key in message:
- template[key] = schema_from_value(message[key])
- return template
-
- def __setitem__(self, key: str, value: JSONSchema) -> None:
- """Check key and value before putting pair into dict.
-
- >>> t = MessageTemplate()
- >>> t['key 1'] = {'const': 'value'}
- >>> t['key 2'] = {'type': 'string'}
- >>> t['key 3'] = {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}}
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
- >>> t[42] = {'const': 'int key'}
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t['key'] = 'schema' # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
- """
- if not isinstance(key, str):
- raise TypeError(f"'{key}' is not a valid key in MessageTemplate"
- " (not a string).")
- try:
- jsonschema.Draft7Validator.check_schema(value)
- # Draft7Validator is hardcoded, because _LATEST_VERSION is
- # non-public in jsonschema and we also perhaps do not want to
- # upgrade automatically.
- except jsonschema.exceptions.SchemaError:
- raise TypeError(f"'{value}' is not a valid value in"
- " MessageTemplate (not a valid JSON schema).")
- super().__setitem__(key, value)
-
- def update(self, *args, **kwargs) -> None:
- """Override update to use validity checks.
-
- >>> t = MessageTemplate()
- >>> t.update({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}}})
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
- >>> t.update({42: {'const': 'int key'}})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t.update({'key': 'schema'}) # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
-
- This is also used in __init__:
- >>> t = MessageTemplate({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {
- ... 'key 1': {'type': 'number'},
- ... 'key 2': True}}})
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
- >>> t = MessageTemplate({42: {'const': 'int key'}})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t = MessageTemplate({'key': 'schema'})
- ... # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
- """
- if args:
- if len(args) > 1:
- raise TypeError("update expected at most 1 argument,"
- f" got {len(args)}")
- other = dict(args[0])
- for key in other:
- self[key] = other[key]
- for key in kwargs:
- self[key] = kwargs[key]
-
- def setdefault(self, key: str, value: JSONSchema = None) -> JSONSchema:
- """Override setdefault to use validity checks.
-
- >>> t = MessageTemplate()
- >>> t.setdefault('key 1', {'const': 'value'})
- {'const': 'value'}
- >>> t.setdefault('key 2', {'type': 'string'})
- {'type': 'string'}
- >>> t.setdefault('key 3', {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}})
- ... # doctest: +NORMALIZE_WHITESPACE
- {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}
- >>> t.setdefault(42, {'const': 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t.setdefault('key', 'schema') # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
-
- But __setitem__ is not called if the key is already present:
- >>> t.setdefault('key 1', 'schema')
- {'const': 'value'}
- """
- if key not in self:
- if value is not None:
- self[key] = value
- else:
- self[key] = True
- return self[key]
-
- def check(self, message: Message) -> bool:
- """Check message against this template.
-
- Constant values have to match exactly:
- >>> t = MessageTemplate({'key': {'const': 'value'}})
- >>> t.check(Message('Example Sender', {'key': 'value'}))
- True
- >>> t.check(Message('Example Sender', {'key': 'other value'}))
- False
-
- But for integers, floats with the same value are also valid:
- >>> t = MessageTemplate({'key': {'const': 42}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.0}))
- True
-
- Type integer is valid for floats with zero fractional part, but
- not by floats with non-zero fractional part:
- >>> t = MessageTemplate({'key': {'type': 'integer'}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.0}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.42}))
- False
-
- Type number is valid for arbitrary ints or floats:
- >>> t = MessageTemplate({'key': {'type': 'number'}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.42}))
- True
-
- All keys in template have to be present in message:
- >>> t = MessageTemplate({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {
- ... 'key 1': {'type': 'number'},
- ... 'key 2': True,
- ... 'key 3': False}}})
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string'}))
- False
-
- But for nested objects their properties do not necessarily have
- to be present:
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 1': 42}}))
- True
-
- Schema True matches everything (even None):
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 2': None}}))
- True
-
- Schema False matches nothing:
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 3': True}}))
- False
-
- Message is valid for the constant template created from it:
- >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
- ... 'list': [None, True, 'string']})
- >>> t = MessageTemplate.from_message(m)
- >>> t.check(m)
- True
- """
- for key in self:
- if key not in message:
- return False
- else:
- validator = jsonschema.Draft7Validator(self[key])
- for error in validator.iter_errors(message[key]):
- return False
- return True
-
-
-class MessageTemplateRegistry:
- """Manage a collection of message templates with registered clients.
-
- A new MessageTemplateRegistry is created by:
- >>> r = MessageTemplateRegistry()
-
- Client names (strings) can be registered for message templates, which
- are mappings from keys to JSON schemas:
- >>> r.insert({'k1': {'const': 'v1'}}, 'C 1')
-
- The check function checks if the templates registered for a client
- match a given message:
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 1', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 2}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 2}: False
-
- Clients can be registered for values validating against arbitrary JSON
- schemas, e.g. all values of a certain type:
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}, 'C 2')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 2', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 2}: False
- {'k1': 'v2', 'k2': 'v1'}: True
- {'k1': 'v2', 'k2': 2}: False
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}, 'C 3')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 3', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 2}: False
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 2}: True
-
- The order of key-value pairs does not have to match the order in the
- messages and keys can be left out:
- >>> r.insert({'k2': {'const': 2}}, 'C 4')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 4', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 2}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 2}: True
-
- A registration for an empty template matches all messages:
- >>> r.insert({}, 'C 5')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 5', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 2}: True
- {'k1': 'v2', 'k2': 'v1'}: True
- {'k1': 'v2', 'k2': 2}: True
-
- A client can be registered for multiple templates:
- >>> r.insert({'k1': {'const': 'v1'}}, 'C 6')
- >>> r.insert({'k2': {'const': 'v1'}}, 'C 6')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 6', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 2}: True
- {'k1': 'v2', 'k2': 'v1'}: True
- {'k1': 'v2', 'k2': 2}: False
-
- Clients can be deregistered again (the result is False if the registry
- is empty after the deletion):
- >>> r.insert({'k1': {'const': 'v1'}}, 'C 7')
- >>> r.delete('C 7')
- True
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 7', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 2}: False
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 2}: False
-
- The get function returns all clients with registered templates matching
- a given message:
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.get(m)}")
- {'k1': 'v1', 'k2': 'v1'}: ['C 5', 'C 1', 'C 6']
- {'k1': 'v1', 'k2': 2}: ['C 5', 'C 1', 'C 6', 'C 4']
- {'k1': 'v2', 'k2': 'v1'}: ['C 5', 'C 2', 'C 6']
- {'k1': 'v2', 'k2': 2}: ['C 5', 'C 3', 'C 4']
-
- The get_templates function returns all templates for a given client:
- >>> for c in ['C 1', 'C 2', 'C 3', 'C 4', 'C 5', 'C 6']:
- ... print(f"{c}: {r.get_templates(c)}")
- C 1: [{'k1': {'const': 'v1'}}]
- C 2: [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
- C 3: [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
- C 4: [{'k2': {'const': 2}}]
- C 5: [{}]
- C 6: [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
- """
-
- def __init__(self) -> None:
- """Initialise an empty registry.
-
- >>> r = MessageTemplateRegistry()
- """
- self._clients: List[str] = []
- self._children: Dict[str, Dict[str, MessageTemplateRegistry]] = {}
-
- def insert(self, template: MessageTemplate, client: str) -> None:
- """Register a client for a template.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v1'}}, 'C 1')
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v2'}}, 'C 2')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v1'}}, 'C 3')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v2'}}, 'C 4')
- >>> r.insert({}, 'C 5')
-
- Implementation details:
- -----------------------
- The tree nodes on the way to a registered object are used/created
- in the order given in the message template, which can be used to
- design more efficient lookups (e.g., putting rarer key-value pairs
- earlier in the template).
- >>> r._clients
- ['C 5']
- >>> r._children.keys()
- dict_keys(['k1'])
- >>> r._children['k1'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> r._children['k1']['{"const": "v1"}']._clients
- []
- >>> r._children['k1']['{"const": "v1"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v1"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 1']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 2']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- >>> r._children['k1']['{"const": "v2"}']._clients
- []
- >>> r._children['k1']['{"const": "v2"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v2"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 3']
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 4']
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- """
- if not template:
- self._clients.append(client)
- else:
- key, schema = next(iter(template.items()))
- schema_string = json.dumps(schema)
- reduced_template = MessageTemplate({k: template[k]
- for k in template
- if k != key})
- if key not in self._children:
- self._children[key] = {}
- if schema_string not in self._children[key]:
- self._children[key][schema_string] = MessageTemplateRegistry()
- self._children[key][schema_string].insert(reduced_template, client)
-
- def delete(self, client: str) -> bool:
- """Unregister a client from all templates.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v1'}}, 'C 1')
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v2'}}, 'C 2')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v1'}}, 'C 3')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v2'}}, 'C 4')
- >>> r.insert({}, 'C 5')
- >>> r.delete('C 3')
- True
- >>> r.delete('C 4')
- True
-
- Implementation details:
- -----------------------
- If parts of the tree become superfluous by the deletion of the
- client, they are also completely removed to reduce the lookup
- effort and keep the tree clean.
- >>> r._clients
- ['C 5']
- >>> r._children.keys()
- dict_keys(['k1'])
- >>> r._children['k1'].keys()
- dict_keys(['{"const": "v1"}'])
- >>> r._children['k1']['{"const": "v1"}']._clients
- []
- >>> r._children['k1']['{"const": "v1"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v1"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 1']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 2']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- """
- self._clients = [c for c in self._clients if c != client]
- new_children: Dict[str, Dict[str, MessageTemplateRegistry]] = {}
- for key in self._children:
- new_children[key] = {}
- for schema in self._children[key]:
- if self._children[key][schema].delete(client):
- new_children[key][schema] = self._children[key][schema]
- if not new_children[key]:
- del new_children[key]
- self._children = new_children
- if self._clients or self._children:
- return True
- return False
-
- def check(self, client: str, message: Message) -> bool:
- """Get if a client has a registered template matching a message.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.check('Client 1', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 'v2'}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 'v2'}: False
- >>> r.insert({'k2': {'const': 'v2'}}, 'Client 2')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.check('Client 2', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 'v2'}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 'v2'}: True
- """
- if client in self._clients:
- return True
- for key in self._children:
- if key in message:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- validator = jsonschema.Draft7Validator(schema)
- validated = True
- for error in validator.iter_errors(message[key]):
- validated = False
- if validated:
- child = self._children[key][schema_string]
- if child.check(client, message):
- return True
- return False
-
- def get(self, message: Message) -> List[str]:
- """Get all clients registered for templates matching a message.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> r.insert({'k2': {'const': 'v2'}}, 'Client 2')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.get(m)}")
- {'k1': 'v1', 'k2': 'v1'}: ['Client 1']
- {'k1': 'v1', 'k2': 'v2'}: ['Client 1', 'Client 2']
- {'k1': 'v2', 'k2': 'v1'}: []
- {'k1': 'v2', 'k2': 'v2'}: ['Client 2']
- """
- result = []
- for client in self._clients:
- if client not in result:
- result.append(client)
- for key in self._children:
- if key in message:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- validator = jsonschema.Draft7Validator(schema)
- validated = True
- for error in validator.iter_errors(message[key]):
- validated = False
- if validated:
- child = self._children[key][schema_string]
- for client in child.get(message):
- if client not in result:
- result.append(client)
- return result
-
- def get_templates(self, client: str) -> List[MessageTemplate]:
- """Get all templates for a client.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> r.get_templates('Client 1')
- [{'k1': {'const': 'v1'}}]
- >>> r.insert({'k1': {'const': 'v2'},
- ... 'k2': {'type': 'string'}}, 'Client 2')
- >>> r.get_templates('Client 2')
- [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
- >>> r.insert({'k1': {'const': 'v2'},
- ... 'k2': {'type': 'integer'}}, 'Client 3')
- >>> r.get_templates('Client 3')
- [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
- >>> r.insert({'k2': {'const': 2}}, 'Client 4')
- >>> r.get_templates('Client 4')
- [{'k2': {'const': 2}}]
- >>> r.insert({}, 'Client 5')
- >>> r.get_templates('Client 5')
- [{}]
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 6')
- >>> r.insert({'k2': {'const': 'v1'}}, 'Client 6')
- >>> r.get_templates('Client 6')
- [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
- """
- result = []
- if client in self._clients:
- result.append(MessageTemplate())
- for key in self._children:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- child = self._children[key][schema_string]
- for template in child.get_templates(client):
- current = MessageTemplate({key: schema})
- current.update(template)
- result.append(current)
- return result
-
-
-class BusException(Exception):
- """Raise for errors in using message bus."""
-
-
-class MessageBus:
- """Provide an asynchronous message bus.
-
- The bus executes asynchronous callbacks for all messages to be received
- by a client. We use a simple callback printing the message in all
- examples:
- >>> def callback_for_receiver(receiver):
- ... print(f"Creating callback for {receiver}.")
- ... async def callback(message):
- ... print(f"{receiver}: {message}")
- ... return callback
-
- Clients can be registered at the bus with a name, lists of message
- templates they want to use for sending and receiving and a callback
- function for receiving. An empty list of templates means that the
- client does not want to send or receive any messages, respectively.
- A list with an empty template means that it wants to send arbitrary
- or receive all messages, respectively:
- >>> async def setup(bus):
- ... print("Setting up.")
- ... bus.register('Logger', 'Test Plugin',
- ... [],
- ... [{}],
- ... callback_for_receiver('Logger'))
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... [{'target': {'const': 'Client 1'}}],
- ... callback_for_receiver('Client 1'))
- ... bus.register('Client 2', 'Test Plugin',
- ... [{}],
- ... [{'target': {'const': 'Client 2'}}],
- ... callback_for_receiver('Client 2'))
-
- The bus itself is addressed by the empty string. It sends messages for
- each registration and deregestration of a client with a key 'event' and
- a value of 'registered' or 'unregistered', a key 'client' with the
- client's name as value and for registrations also keys 'sends' and
- 'receives' with all templates registered for the client for sending and
- receiving.
-
- Clients can send to the bus with the send function. Each message has to
- declare a sender. The send templates of that sender are checked for a
- template matching the message. We cannot prevent arbitrary code from
- impersonating any sender, but this should only be done in debugging or
- management situations.
-
- Messages that are intended for a specific client by convention have a
- key 'target' with the target client's name as value. Such messages are
- often commands to the client to do something, which is by convention
- indicated by a key 'command' with a value that indicates what should be
- done.
-
- The bus, for example, reacts to a message with 'target': '' and
- 'command': 'get clients' by sending one message for each currently
- registered with complete information about its registered send and
- receive templates.
-
- >>> async def send(bus):
- ... print("Sending messages.")
- ... await bus.send({'sender': 'Client 1', 'k1': 'Test'})
- ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
- ... await bus.send({'sender': '', 'target': '',
- ... 'command': 'get clients'})
-
- The run function executes the message bus forever. If we want to stop
- it, we have to explicitly cancel the task:
- >>> async def main():
- ... bus = MessageBus()
- ... await setup(bus)
- ... bus_task = asyncio.create_task(bus.run())
- ... await send(bus)
- ... await asyncio.sleep(0)
- ... bus_task.cancel()
- >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
- Setting up.
- Creating callback for Logger.
- Creating callback for Client 1.
- Creating callback for Client 2.
- Sending messages.
- Logger: {'sender': '', 'event': 'registered',
- 'client': 'Logger', 'plugin': 'Test Plugin',
- 'sends': [], 'receives': [{}]}
- Logger: {'sender': '', 'event': 'registered',
- 'client': 'Client 1', 'plugin': 'Test Plugin',
- 'sends': [{'k1': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Client 1'}}]}
- Logger: {'sender': '', 'event': 'registered',
- 'client': 'Client 2', 'plugin': 'Test Plugin',
- 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
- Logger: {'sender': 'Client 1', 'k1': 'Test'}
- Logger: {'sender': 'Client 2', 'target': 'Client 1'}
- Client 1: {'sender': 'Client 2', 'target': 'Client 1'}
- Logger: {'sender': '', 'target': '', 'command': 'get clients'}
- Logger: {'sender': '', 'client': 'Logger', 'plugin': 'Test Plugin',
- 'sends': [], 'receives': [{}]}
- Logger: {'sender': '', 'client': 'Client 1', 'plugin': 'Test Plugin',
- 'sends': [{'k1': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Client 1'}}]}
- Logger: {'sender': '', 'client': 'Client 2', 'plugin': 'Test Plugin',
- 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
- """
-
- def __init__(self) -> None:
- """Initialise a new bus without clients.
-
- >>> async def main():
- ... bus = MessageBus()
- >>> asyncio.run(main())
- """
- self._queue: asyncio.Queue = asyncio.Queue()
- self._plugins: Dict[str, str] = {}
- self._send_reg: MessageTemplateRegistry = MessageTemplateRegistry()
- self._recv_reg: MessageTemplateRegistry = MessageTemplateRegistry()
- self._callbacks: Dict[str, MessageCallback] = {}
-
- def register(self, client: str, plugin: str,
- sends: Iterable[MessageTemplate],
- receives: Iterable[MessageTemplate],
- callback: MessageCallback) -> None:
- """Register a client at the message bus.
-
- >>> async def callback(message):
- ... print(message)
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Logger', 'Test Plugin',
- ... [], # send nothing
- ... [{}], # receive everything
- ... callback)
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... # send with key 'k1' and string value
- ... [{'target': {'const': 'Client 1'}}],
- ... # receive for this client
- ... callback)
- ... bus.register('Client 2', 'Test Plugin',
- ... [{}], # send arbitrary
- ... [{'target': {'const': 'Client 2'}}],
- ... # receive for this client
- ... callback)
- >>> asyncio.run(main())
- """
- if not client:
- raise BusException("Client name is not allowed to be empty.")
- if client in self._plugins:
- raise BusException(f"Client '{client}' already registered"
- " at message bus.")
- event = Message('')
- event['event'] = 'registered'
- event['client'] = client
- self._plugins[client] = plugin
- event['plugin'] = plugin
- for template in sends:
- self._send_reg.insert(template, client)
- event['sends'] = self._send_reg.get_templates(client)
- for template in receives:
- self._recv_reg.insert(template, client)
- event['receives'] = self._recv_reg.get_templates(client)
- self._callbacks[client] = callback
- self._queue.put_nowait(event)
-
- def unregister(self, client: str) -> None:
- """Unregister a client from the message bus.
-
- >>> async def callback(message):
- ... print(message)
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... [{'target': {'const': 'Client 1'}}],
- ... callback)
- ... bus.unregister('Client 1')
- >>> asyncio.run(main())
- """
- if client not in self._plugins:
- return
- event = Message('')
- event['event'] = 'unregistered'
- event['client'] = client
- del self._plugins[client]
- self._send_reg.delete(client)
- self._recv_reg.delete(client)
- if client in self._callbacks:
- del self._callbacks[client]
- self._queue.put_nowait(event)
-
- async def run(self) -> None:
- """Run the message bus forever.
-
- >>> async def main():
- ... bus = MessageBus()
- ... bus_task = asyncio.create_task(bus.run())
- ... bus_task.cancel()
- >>> asyncio.run(main())
- """
- while True:
- message = await self._queue.get()
- if ('target' in message and
- message['target'] == '' and
- 'command' in message and
- message['command'] == 'get clients'):
- for client in self._plugins:
- answer = Message('')
- answer['client'] = client
- answer['plugin'] = self._plugins[client]
- answer['sends'] = self._send_reg.get_templates(client)
- answer['receives'] = self._recv_reg.get_templates(client)
- await self._queue.put(answer)
- for client in self._recv_reg.get(message):
- await self._callbacks[client](message)
- self._queue.task_done()
-
- async def send(self, message: Message) -> None:
- """Send a message to the message bus.
-
- >>> async def callback(message):
- ... print(f"Got: {message}")
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... [{'target': {'const': 'Client 1'}}],
- ... callback)
- ... bus.register('Client 2', 'Test Plugin',
- ... [{}],
- ... [{'target': {'const': 'Client 2'}}],
- ... callback)
- ... bus_task = asyncio.create_task(bus.run())
- ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
- ... 'k1': 'Test'})
- ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
- ... try:
- ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
- ... 'k1': 42})
- ... except BusException as e:
- ... print(e)
- ... await asyncio.sleep(0)
- ... bus_task.cancel()
- >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
- Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
- not allowed for sender 'Client 1'.
- Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
- Got: {'sender': 'Client 2', 'target': 'Client 1'}
- """
- assert isinstance(message['sender'], str)
- sender = message['sender']
- if sender:
- if not self._send_reg.check(sender, message):
- raise BusException(f"Message '{message}' not allowed for"
- f" sender '{sender}'.")
- await self._queue.put(message)
-
-
-
-
-
-
-
-
-
-class Message
-( sender: str, init: Dict[str, Union[NoneType, str, int, float, bool, Dict[str, Any], List[Any]]] = None)
-
-
-Define arbitrary message.
-
Messages are dictionaries with string keys and values that are strings,
-integers, floats, Booleans, dictionaries that recursively have string
-keys and values of any of these types, or lists with elements that have
-any of these types. These constraints are checked when setting key-value
-pairs of the message.
-
A message has to have a sender, which is set by the constructor:
-
>>> m = Message('Example sender')
->>> print(m)
-{'sender': 'Example sender'}
-
-
A dictionary can be given to the constructor:
-
>>> m = Message('Example sender', {'key 1': 'value 1', 'key 2': 'value 2'})
->>> print(m)
-{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
-
-
Or the message can be modified after construction:
-
>>> m = Message('Example sender', {'key 1': 'value 1'})
->>> m['key 2'] = 'value 2'
->>> print(m)
-{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
-
-
Initialise message.
-
Message is initialised with given sender and possibly given
-key-value pairs:
-
>>> m = Message('Example sender')
->>> print(m)
-{'sender': 'Example sender'}
->>> m = Message('Example sender', {'key 1': 'value 1'})
->>> print(m)
-{'sender': 'Example sender', 'key 1': 'value 1'}
-
-
The sender can be overwritten by the key-value pairs:
-
>>> m = Message('Example sender', {'sender': 'Another sender'})
->>> print(m)
-{'sender': 'Another sender'}
-
-
-
-Expand source code
-
-class Message(Dict[str, MessageValue]):
- """Define arbitrary message.
-
- Messages are dictionaries with string keys and values that are strings,
- integers, floats, Booleans, dictionaries that recursively have string
- keys and values of any of these types, or lists with elements that have
- any of these types. These constraints are checked when setting key-value
- pairs of the message.
-
- A message has to have a sender, which is set by the constructor:
- >>> m = Message('Example sender')
- >>> print(m)
- {'sender': 'Example sender'}
-
- A dictionary can be given to the constructor:
- >>> m = Message('Example sender', {'key 1': 'value 1', 'key 2': 'value 2'})
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
-
- Or the message can be modified after construction:
- >>> m = Message('Example sender', {'key 1': 'value 1'})
- >>> m['key 2'] = 'value 2'
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
- """
-
- def __init__(self, sender: str,
- init: Dict[str, MessageValue] = None) -> None:
- """Initialise message.
-
- Message is initialised with given sender and possibly given
- key-value pairs:
- >>> m = Message('Example sender')
- >>> print(m)
- {'sender': 'Example sender'}
- >>> m = Message('Example sender', {'key 1': 'value 1'})
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1'}
-
- The sender can be overwritten by the key-value pairs:
- >>> m = Message('Example sender', {'sender': 'Another sender'})
- >>> print(m)
- {'sender': 'Another sender'}
- """
- if not isinstance(sender, str):
- raise TypeError(f"'{sender}' is not a valid sender name"
- " (not a string).")
- self['sender'] = sender
- if init is not None:
- self.update(init)
-
- @staticmethod
- def check_value(value: MessageValue) -> bool:
- """Check recursively if a given value is valid.
-
- None, strings, integers, floats and Booleans are valid:
- >>> Message.check_value(None)
- True
- >>> Message.check_value('Spam')
- True
- >>> Message.check_value(42)
- True
- >>> Message.check_value(42.42)
- True
- >>> Message.check_value(False)
- True
-
- Other basic types are not valid:
- >>> Message.check_value(b'bytes')
- False
- >>> Message.check_value(1j)
- False
-
- Dictionaries with string keys and recursively valid values are valid:
- >>> Message.check_value({'str value': 'Spam', 'int value': 42,
- ... 'float value': 42.42, 'bool value': False})
- True
-
- Empty dictionaries are valid:
- >>> Message.check_value({})
- True
-
- Dictionaries with other keys are not valid:
- >>> Message.check_value({42: 'int key'})
- False
-
- Dictionaries with invalid values are not valid:
- >>> Message.check_value({'complex value': 1j})
- False
-
- Lists with valid elements are valid:
- >>> Message.check_value(['Spam', 42, 42.42, False])
- True
-
- Empty lists are valid:
- >>> Message.check_value([])
- True
-
- Lists with invalid elements are not valid:
- >>> Message.check_value([1j])
- False
- """
- if value is None:
- return True
- elif (isinstance(value, str) or isinstance(value, int) or
- isinstance(value, float) or isinstance(value, bool)):
- return True
- elif isinstance(value, dict):
- for key in value:
- if not isinstance(key, str):
- return False
- if not Message.check_value(value[key]):
- return False
- return True
- elif isinstance(value, list):
- for element in value:
- if not Message.check_value(element):
- return False
- return True
- return False
-
- def __setitem__(self, key: str, value: MessageValue) -> None:
- """Check key and value before putting pair into dict.
-
- >>> m = Message('Example sender')
- >>> m['key'] = 'value'
- >>> m['dict'] = {'k1': 'v1', 'k2': 2}
- >>> print(m) # doctest: +NORMALIZE_WHITESPACE
- {'sender': 'Example sender', 'key': 'value',
- 'dict': {'k1': 'v1', 'k2': 2}}
- >>> m = Message('Example sender')
- >>> m[42] = 'int key'
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m['complex value'] = 1j
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
- """
- if not isinstance(key, str):
- raise TypeError(f"'{key}' is not a valid key in Message"
- " (not a string).")
- if not self.check_value(value):
- raise TypeError(f"'{value}' is not a valid value in Message.")
- super().__setitem__(key, value)
-
- def update(self, *args, **kwargs) -> None:
- """Override update to use validity checks.
-
- >>> m = Message('Example sender')
- >>> m.update({'key 1': 'value 1', 'key 2': 'value 2'})
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
- >>> m.update({42: 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m.update({'complex value': 1j})
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
-
- This is also used in __init__:
- >>> m = Message('Example sender', {'key': 'value'})
- >>> print(m)
- {'sender': 'Example sender', 'key': 'value'}
- >>> m = Message('Example sender', {42: 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m = Message('Example sender', {'complex value': 1j})
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
- """
- if args:
- if len(args) > 1:
- raise TypeError("update expected at most 1 argument,"
- f" got {len(args)}")
- other = dict(args[0])
- for key in other:
- self[key] = other[key]
- for key in kwargs:
- self[key] = kwargs[key]
-
- def setdefault(self, key: str, value: MessageValue = None) -> MessageValue:
- """Override setdefault to use validity checks.
-
- >>> m = Message('Example sender')
- >>> m.setdefault('key', 'value 1')
- 'value 1'
- >>> m.setdefault('key', 'value 2')
- 'value 1'
- >>> m.setdefault(42, 'int key')
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m.setdefault('complex value', 1j)
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
-
- But __setitem__ is not called if the key is already present:
- >>> m.setdefault('key', 1j)
- 'value 1'
- """
- if key not in self:
- self[key] = value
- return self[key]
-
-Ancestors
-
-builtins.dict
-typing.Generic
-
-Static methods
-
-
-def check_value (value: Union[NoneType, str, int, float, bool, Dict[str, Any], List[Any]]) â> bool
-
-
-Check recursively if a given value is valid.
-
None, strings, integers, floats and Booleans are valid:
-
>>> Message.check_value(None)
-True
->>> Message.check_value('Spam')
-True
->>> Message.check_value(42)
-True
->>> Message.check_value(42.42)
-True
->>> Message.check_value(False)
-True
-
-
Other basic types are not valid:
-
>>> Message.check_value(b'bytes')
-False
->>> Message.check_value(1j)
-False
-
-
Dictionaries with string keys and recursively valid values are valid:
-
>>> Message.check_value({'str value': 'Spam', 'int value': 42,
-... 'float value': 42.42, 'bool value': False})
-True
-
-
Empty dictionaries are valid:
-
>>> Message.check_value({})
-True
-
-
Dictionaries with other keys are not valid:
-
>>> Message.check_value({42: 'int key'})
-False
-
-
Dictionaries with invalid values are not valid:
-
>>> Message.check_value({'complex value': 1j})
-False
-
-
Lists with valid elements are valid:
-
>>> Message.check_value(['Spam', 42, 42.42, False])
-True
-
-
Empty lists are valid:
-
>>> Message.check_value([])
-True
-
-
Lists with invalid elements are not valid:
-
>>> Message.check_value([1j])
-False
-
-
-
-Expand source code
-
-@staticmethod
-def check_value(value: MessageValue) -> bool:
- """Check recursively if a given value is valid.
-
- None, strings, integers, floats and Booleans are valid:
- >>> Message.check_value(None)
- True
- >>> Message.check_value('Spam')
- True
- >>> Message.check_value(42)
- True
- >>> Message.check_value(42.42)
- True
- >>> Message.check_value(False)
- True
-
- Other basic types are not valid:
- >>> Message.check_value(b'bytes')
- False
- >>> Message.check_value(1j)
- False
-
- Dictionaries with string keys and recursively valid values are valid:
- >>> Message.check_value({'str value': 'Spam', 'int value': 42,
- ... 'float value': 42.42, 'bool value': False})
- True
-
- Empty dictionaries are valid:
- >>> Message.check_value({})
- True
-
- Dictionaries with other keys are not valid:
- >>> Message.check_value({42: 'int key'})
- False
-
- Dictionaries with invalid values are not valid:
- >>> Message.check_value({'complex value': 1j})
- False
-
- Lists with valid elements are valid:
- >>> Message.check_value(['Spam', 42, 42.42, False])
- True
-
- Empty lists are valid:
- >>> Message.check_value([])
- True
-
- Lists with invalid elements are not valid:
- >>> Message.check_value([1j])
- False
- """
- if value is None:
- return True
- elif (isinstance(value, str) or isinstance(value, int) or
- isinstance(value, float) or isinstance(value, bool)):
- return True
- elif isinstance(value, dict):
- for key in value:
- if not isinstance(key, str):
- return False
- if not Message.check_value(value[key]):
- return False
- return True
- elif isinstance(value, list):
- for element in value:
- if not Message.check_value(element):
- return False
- return True
- return False
-
-
-
-Methods
-
-
-def update (self, *args, **kwargs) â>Â NoneType
-
-
-Override update to use validity checks.
-
>>> m = Message('Example sender')
->>> m.update({'key 1': 'value 1', 'key 2': 'value 2'})
->>> print(m)
-{'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
->>> m.update({42: 'int key'})
-Traceback (most recent call last):
- ...
-TypeError: '42' is not a valid key in Message (not a string).
->>> m.update({'complex value': 1j})
-Traceback (most recent call last):
- ...
-TypeError: '1j' is not a valid value in Message.
-
-
This is also used in init :
-
>>> m = Message('Example sender', {'key': 'value'})
->>> print(m)
-{'sender': 'Example sender', 'key': 'value'}
->>> m = Message('Example sender', {42: 'int key'})
-Traceback (most recent call last):
- ...
-TypeError: '42' is not a valid key in Message (not a string).
->>> m = Message('Example sender', {'complex value': 1j})
-Traceback (most recent call last):
- ...
-TypeError: '1j' is not a valid value in Message.
-
-
-
-Expand source code
-
-def update(self, *args, **kwargs) -> None:
- """Override update to use validity checks.
-
- >>> m = Message('Example sender')
- >>> m.update({'key 1': 'value 1', 'key 2': 'value 2'})
- >>> print(m)
- {'sender': 'Example sender', 'key 1': 'value 1', 'key 2': 'value 2'}
- >>> m.update({42: 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m.update({'complex value': 1j})
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
-
- This is also used in __init__:
- >>> m = Message('Example sender', {'key': 'value'})
- >>> print(m)
- {'sender': 'Example sender', 'key': 'value'}
- >>> m = Message('Example sender', {42: 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m = Message('Example sender', {'complex value': 1j})
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
- """
- if args:
- if len(args) > 1:
- raise TypeError("update expected at most 1 argument,"
- f" got {len(args)}")
- other = dict(args[0])
- for key in other:
- self[key] = other[key]
- for key in kwargs:
- self[key] = kwargs[key]
-
-
-
-def setdefault (self, key: str, value: Union[NoneType, str, int, float, bool, Dict[str, Any], List[Any]] = None) â> Union[NoneType, str, int, float, bool, Dict[str, Any], List[Any]]
-
-
-Override setdefault to use validity checks.
-
>>> m = Message('Example sender')
->>> m.setdefault('key', 'value 1')
-'value 1'
->>> m.setdefault('key', 'value 2')
-'value 1'
->>> m.setdefault(42, 'int key')
-Traceback (most recent call last):
- ...
-TypeError: '42' is not a valid key in Message (not a string).
->>> m.setdefault('complex value', 1j)
-Traceback (most recent call last):
- ...
-TypeError: '1j' is not a valid value in Message.
-
-
But setitem is not called if the key is already present:
-
>>> m.setdefault('key', 1j)
-'value 1'
-
-
-
-Expand source code
-
-def setdefault(self, key: str, value: MessageValue = None) -> MessageValue:
- """Override setdefault to use validity checks.
-
- >>> m = Message('Example sender')
- >>> m.setdefault('key', 'value 1')
- 'value 1'
- >>> m.setdefault('key', 'value 2')
- 'value 1'
- >>> m.setdefault(42, 'int key')
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in Message (not a string).
- >>> m.setdefault('complex value', 1j)
- Traceback (most recent call last):
- ...
- TypeError: '1j' is not a valid value in Message.
-
- But __setitem__ is not called if the key is already present:
- >>> m.setdefault('key', 1j)
- 'value 1'
- """
- if key not in self:
- self[key] = value
- return self[key]
-
-
-
-
-
-class MessageTemplate
-( init: Dict[str, Union[bool, Dict[str, Union[NoneType, str, int, float, bool, Dict[str, Any], List[Any]]]]] = None)
-
-
-Define a message template.
-
A message template is a mapping from string keys to JSON schemas as
-values:
-
>>> t = MessageTemplate({'key 1': {'const': 'value'},
-... 'key 2': {'type': 'string'}})
->>> t['key 3'] = {'type': 'object',
-... 'properties': {'key 1': {'type': 'number'},
-... 'key 2': True}}
-
-
A message template matches a message if all keys of the template are
-contained in the message and the values in the message validate against
-the respective schemas:
-
>>> t.check(Message('Example Sender',
-... {'key 1': 'value', 'key 2': 'some string',
-... 'key 3': {'key 1': 42, 'key 2': None}}))
-True
-
-
An empty mapping therefore matches all messages:
-
>>> t = MessageTemplate()
->>> t.check(Message('Example Sender', {'arbitrary': 'content'}))
-True
-
-
Initialise message.
-
Template is initialised empty or with given key-value pairs:
-
>>> t = MessageTemplate()
->>> print(t)
-{}
->>> t = MessageTemplate({'key': {'const': 'value'}})
->>> print(t)
-{'key': {'const': 'value'}}
-
-
-
-Expand source code
-
-class MessageTemplate(Dict[str, JSONSchema]):
- """Define a message template.
-
- A message template is a mapping from string keys to JSON schemas as
- values:
- >>> t = MessageTemplate({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'}})
- >>> t['key 3'] = {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}}
-
- A message template matches a message if all keys of the template are
- contained in the message and the values in the message validate against
- the respective schemas:
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 1': 42, 'key 2': None}}))
- True
-
- An empty mapping therefore matches all messages:
- >>> t = MessageTemplate()
- >>> t.check(Message('Example Sender', {'arbitrary': 'content'}))
- True
- """
-
- def __init__(self, init: Dict[str, JSONSchema] = None) -> None:
- """Initialise message.
-
- Template is initialised empty or with given key-value pairs:
- >>> t = MessageTemplate()
- >>> print(t)
- {}
- >>> t = MessageTemplate({'key': {'const': 'value'}})
- >>> print(t)
- {'key': {'const': 'value'}}
- """
- if init is not None:
- self.update(init)
-
- @staticmethod
- def from_message(message: Message) -> 'MessageTemplate':
- """Create template from message.
-
- Template witch constant schemas is created from message:
- >>> m = Message('Example Sender', {'key': 'value'})
- >>> t = MessageTemplate.from_message(m)
- >>> print(t)
- {'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}}
- >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
- ... 'list': [None, True, 'string']})
- >>> t = MessageTemplate.from_message(m)
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'sender': {'const': 'Example Sender'},
- 'dict': {'type': 'object',
- 'properties': {'int': {'const': 42},
- 'float': {'const': 42.42}}},
- 'list': {'type': 'array',
- 'items': [{'const': None},
- {'const': True},
- {'const': 'string'}]}}
-
- This is especially useful for clients that send certain fully
- predefined messages, where the message is given in the configuration
- and the template for the registration can be constructed by this
- method.
- """
- def schema_from_value(value: MessageValue) -> JSONSchema:
- schema: JSONSchema = False
- if value is None:
- schema = {'const': None}
- elif (isinstance(value, str) or isinstance(value, int) or
- isinstance(value, float) or isinstance(value, bool)):
- schema = {'const': value}
- elif isinstance(value, dict):
- properties = {}
- for inner_key in value:
- inner_value: Message = value[inner_key]
- properties[inner_key] = schema_from_value(inner_value)
- schema = {'type': 'object',
- 'properties': properties}
- elif isinstance(value, list):
- schema = {'type': 'array',
- 'items': [schema_from_value(element)
- for element in value]}
- return schema
- template = MessageTemplate()
- for key in message:
- template[key] = schema_from_value(message[key])
- return template
-
- def __setitem__(self, key: str, value: JSONSchema) -> None:
- """Check key and value before putting pair into dict.
-
- >>> t = MessageTemplate()
- >>> t['key 1'] = {'const': 'value'}
- >>> t['key 2'] = {'type': 'string'}
- >>> t['key 3'] = {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}}
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
- >>> t[42] = {'const': 'int key'}
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t['key'] = 'schema' # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
- """
- if not isinstance(key, str):
- raise TypeError(f"'{key}' is not a valid key in MessageTemplate"
- " (not a string).")
- try:
- jsonschema.Draft7Validator.check_schema(value)
- # Draft7Validator is hardcoded, because _LATEST_VERSION is
- # non-public in jsonschema and we also perhaps do not want to
- # upgrade automatically.
- except jsonschema.exceptions.SchemaError:
- raise TypeError(f"'{value}' is not a valid value in"
- " MessageTemplate (not a valid JSON schema).")
- super().__setitem__(key, value)
-
- def update(self, *args, **kwargs) -> None:
- """Override update to use validity checks.
-
- >>> t = MessageTemplate()
- >>> t.update({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}}})
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
- >>> t.update({42: {'const': 'int key'}})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t.update({'key': 'schema'}) # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
-
- This is also used in __init__:
- >>> t = MessageTemplate({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {
- ... 'key 1': {'type': 'number'},
- ... 'key 2': True}}})
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
- >>> t = MessageTemplate({42: {'const': 'int key'}})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t = MessageTemplate({'key': 'schema'})
- ... # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
- """
- if args:
- if len(args) > 1:
- raise TypeError("update expected at most 1 argument,"
- f" got {len(args)}")
- other = dict(args[0])
- for key in other:
- self[key] = other[key]
- for key in kwargs:
- self[key] = kwargs[key]
-
- def setdefault(self, key: str, value: JSONSchema = None) -> JSONSchema:
- """Override setdefault to use validity checks.
-
- >>> t = MessageTemplate()
- >>> t.setdefault('key 1', {'const': 'value'})
- {'const': 'value'}
- >>> t.setdefault('key 2', {'type': 'string'})
- {'type': 'string'}
- >>> t.setdefault('key 3', {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}})
- ... # doctest: +NORMALIZE_WHITESPACE
- {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}
- >>> t.setdefault(42, {'const': 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t.setdefault('key', 'schema') # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
-
- But __setitem__ is not called if the key is already present:
- >>> t.setdefault('key 1', 'schema')
- {'const': 'value'}
- """
- if key not in self:
- if value is not None:
- self[key] = value
- else:
- self[key] = True
- return self[key]
-
- def check(self, message: Message) -> bool:
- """Check message against this template.
-
- Constant values have to match exactly:
- >>> t = MessageTemplate({'key': {'const': 'value'}})
- >>> t.check(Message('Example Sender', {'key': 'value'}))
- True
- >>> t.check(Message('Example Sender', {'key': 'other value'}))
- False
-
- But for integers, floats with the same value are also valid:
- >>> t = MessageTemplate({'key': {'const': 42}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.0}))
- True
-
- Type integer is valid for floats with zero fractional part, but
- not by floats with non-zero fractional part:
- >>> t = MessageTemplate({'key': {'type': 'integer'}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.0}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.42}))
- False
-
- Type number is valid for arbitrary ints or floats:
- >>> t = MessageTemplate({'key': {'type': 'number'}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.42}))
- True
-
- All keys in template have to be present in message:
- >>> t = MessageTemplate({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {
- ... 'key 1': {'type': 'number'},
- ... 'key 2': True,
- ... 'key 3': False}}})
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string'}))
- False
-
- But for nested objects their properties do not necessarily have
- to be present:
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 1': 42}}))
- True
-
- Schema True matches everything (even None):
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 2': None}}))
- True
-
- Schema False matches nothing:
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 3': True}}))
- False
-
- Message is valid for the constant template created from it:
- >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
- ... 'list': [None, True, 'string']})
- >>> t = MessageTemplate.from_message(m)
- >>> t.check(m)
- True
- """
- for key in self:
- if key not in message:
- return False
- else:
- validator = jsonschema.Draft7Validator(self[key])
- for error in validator.iter_errors(message[key]):
- return False
- return True
-
-Ancestors
-
-builtins.dict
-typing.Generic
-
-Static methods
-
-
-def from_message (message:Â Message ) â>Â MessageTemplate
-
-
-Create template from message.
-
Template witch constant schemas is created from message:
-
>>> m = Message('Example Sender', {'key': 'value'})
->>> t = MessageTemplate.from_message(m)
->>> print(t)
-{'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}}
->>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
-... 'list': [None, True, 'string']})
->>> t = MessageTemplate.from_message(m)
->>> print(t) # doctest: +NORMALIZE_WHITESPACE
-{'sender': {'const': 'Example Sender'},
- 'dict': {'type': 'object',
- 'properties': {'int': {'const': 42},
- 'float': {'const': 42.42}}},
- 'list': {'type': 'array',
- 'items': [{'const': None},
- {'const': True},
- {'const': 'string'}]}}
-
-
This is especially useful for clients that send certain fully
-predefined messages, where the message is given in the configuration
-and the template for the registration can be constructed by this
-method.
-
-
-Expand source code
-
-@staticmethod
-def from_message(message: Message) -> 'MessageTemplate':
- """Create template from message.
-
- Template witch constant schemas is created from message:
- >>> m = Message('Example Sender', {'key': 'value'})
- >>> t = MessageTemplate.from_message(m)
- >>> print(t)
- {'sender': {'const': 'Example Sender'}, 'key': {'const': 'value'}}
- >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
- ... 'list': [None, True, 'string']})
- >>> t = MessageTemplate.from_message(m)
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'sender': {'const': 'Example Sender'},
- 'dict': {'type': 'object',
- 'properties': {'int': {'const': 42},
- 'float': {'const': 42.42}}},
- 'list': {'type': 'array',
- 'items': [{'const': None},
- {'const': True},
- {'const': 'string'}]}}
-
- This is especially useful for clients that send certain fully
- predefined messages, where the message is given in the configuration
- and the template for the registration can be constructed by this
- method.
- """
- def schema_from_value(value: MessageValue) -> JSONSchema:
- schema: JSONSchema = False
- if value is None:
- schema = {'const': None}
- elif (isinstance(value, str) or isinstance(value, int) or
- isinstance(value, float) or isinstance(value, bool)):
- schema = {'const': value}
- elif isinstance(value, dict):
- properties = {}
- for inner_key in value:
- inner_value: Message = value[inner_key]
- properties[inner_key] = schema_from_value(inner_value)
- schema = {'type': 'object',
- 'properties': properties}
- elif isinstance(value, list):
- schema = {'type': 'array',
- 'items': [schema_from_value(element)
- for element in value]}
- return schema
- template = MessageTemplate()
- for key in message:
- template[key] = schema_from_value(message[key])
- return template
-
-
-
-Methods
-
-
-def update (self, *args, **kwargs) â>Â NoneType
-
-
-Override update to use validity checks.
-
>>> t = MessageTemplate()
->>> t.update({'key 1': {'const': 'value'},
-... 'key 2': {'type': 'string'},
-... 'key 3': {'type': 'object',
-... 'properties': {'key 1': {'type': 'number'},
-... 'key 2': True}}})
->>> print(t) # doctest: +NORMALIZE_WHITESPACE
-{'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
->>> t.update({42: {'const': 'int key'}})
-Traceback (most recent call last):
- ...
-TypeError: '42' is not a valid key in MessageTemplate (not a string).
->>> t.update({'key': 'schema'}) # doctest: +NORMALIZE_WHITESPACE
-Traceback (most recent call last):
- ...
-TypeError: 'schema' is not a valid value in MessageTemplate
-(not a valid JSON schema).
-
-
This is also used in init :
-
>>> t = MessageTemplate({'key 1': {'const': 'value'},
-... 'key 2': {'type': 'string'},
-... 'key 3': {'type': 'object',
-... 'properties': {
-... 'key 1': {'type': 'number'},
-... 'key 2': True}}})
->>> print(t) # doctest: +NORMALIZE_WHITESPACE
-{'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
->>> t = MessageTemplate({42: {'const': 'int key'}})
-Traceback (most recent call last):
- ...
-TypeError: '42' is not a valid key in MessageTemplate (not a string).
->>> t = MessageTemplate({'key': 'schema'})
-... # doctest: +NORMALIZE_WHITESPACE
-Traceback (most recent call last):
- ...
-TypeError: 'schema' is not a valid value in MessageTemplate
-(not a valid JSON schema).
-
-
-
-Expand source code
-
-def update(self, *args, **kwargs) -> None:
- """Override update to use validity checks.
-
- >>> t = MessageTemplate()
- >>> t.update({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}}})
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
- >>> t.update({42: {'const': 'int key'}})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t.update({'key': 'schema'}) # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
-
- This is also used in __init__:
- >>> t = MessageTemplate({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {
- ... 'key 1': {'type': 'number'},
- ... 'key 2': True}}})
- >>> print(t) # doctest: +NORMALIZE_WHITESPACE
- {'key 1': {'const': 'value'}, 'key 2': {'type': 'string'},
- 'key 3': {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}}
- >>> t = MessageTemplate({42: {'const': 'int key'}})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t = MessageTemplate({'key': 'schema'})
- ... # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
- """
- if args:
- if len(args) > 1:
- raise TypeError("update expected at most 1 argument,"
- f" got {len(args)}")
- other = dict(args[0])
- for key in other:
- self[key] = other[key]
- for key in kwargs:
- self[key] = kwargs[key]
-
-
-
-def setdefault (self, key: str, value: Union[bool, Dict[str, Union[NoneType, str, int, float, bool, Dict[str, Any], List[Any]]]] = None) â> Union[bool, Dict[str, Union[NoneType, str, int, float, bool, Dict[str, Any], List[Any]]]]
-
-
-Override setdefault to use validity checks.
-
>>> t = MessageTemplate()
->>> t.setdefault('key 1', {'const': 'value'})
-{'const': 'value'}
->>> t.setdefault('key 2', {'type': 'string'})
-{'type': 'string'}
->>> t.setdefault('key 3', {'type': 'object',
-... 'properties': {'key 1': {'type': 'number'},
-... 'key 2': True}})
-... # doctest: +NORMALIZE_WHITESPACE
-{'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}
->>> t.setdefault(42, {'const': 'int key'})
-Traceback (most recent call last):
- ...
-TypeError: '42' is not a valid key in MessageTemplate (not a string).
->>> t.setdefault('key', 'schema') # doctest: +NORMALIZE_WHITESPACE
-Traceback (most recent call last):
- ...
-TypeError: 'schema' is not a valid value in MessageTemplate
-(not a valid JSON schema).
-
-
But setitem is not called if the key is already present:
-
>>> t.setdefault('key 1', 'schema')
-{'const': 'value'}
-
-
-
-Expand source code
-
-def setdefault(self, key: str, value: JSONSchema = None) -> JSONSchema:
- """Override setdefault to use validity checks.
-
- >>> t = MessageTemplate()
- >>> t.setdefault('key 1', {'const': 'value'})
- {'const': 'value'}
- >>> t.setdefault('key 2', {'type': 'string'})
- {'type': 'string'}
- >>> t.setdefault('key 3', {'type': 'object',
- ... 'properties': {'key 1': {'type': 'number'},
- ... 'key 2': True}})
- ... # doctest: +NORMALIZE_WHITESPACE
- {'type': 'object',
- 'properties': {'key 1': {'type': 'number'},
- 'key 2': True}}
- >>> t.setdefault(42, {'const': 'int key'})
- Traceback (most recent call last):
- ...
- TypeError: '42' is not a valid key in MessageTemplate (not a string).
- >>> t.setdefault('key', 'schema') # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- TypeError: 'schema' is not a valid value in MessageTemplate
- (not a valid JSON schema).
-
- But __setitem__ is not called if the key is already present:
- >>> t.setdefault('key 1', 'schema')
- {'const': 'value'}
- """
- if key not in self:
- if value is not None:
- self[key] = value
- else:
- self[key] = True
- return self[key]
-
-
-
-def check (self, message:Â Message ) â>Â bool
-
-
-Check message against this template.
-
Constant values have to match exactly:
-
>>> t = MessageTemplate({'key': {'const': 'value'}})
->>> t.check(Message('Example Sender', {'key': 'value'}))
-True
->>> t.check(Message('Example Sender', {'key': 'other value'}))
-False
-
-
But for integers, floats with the same value are also valid:
-
>>> t = MessageTemplate({'key': {'const': 42}})
->>> t.check(Message('Example Sender', {'key': 42}))
-True
->>> t.check(Message('Example Sender', {'key': 42.0}))
-True
-
-
Type integer is valid for floats with zero fractional part, but
-not by floats with non-zero fractional part:
-
>>> t = MessageTemplate({'key': {'type': 'integer'}})
->>> t.check(Message('Example Sender', {'key': 42}))
-True
->>> t.check(Message('Example Sender', {'key': 42.0}))
-True
->>> t.check(Message('Example Sender', {'key': 42.42}))
-False
-
-
Type number is valid for arbitrary ints or floats:
-
>>> t = MessageTemplate({'key': {'type': 'number'}})
->>> t.check(Message('Example Sender', {'key': 42}))
-True
->>> t.check(Message('Example Sender', {'key': 42.42}))
-True
-
-
All keys in template have to be present in message:
-
>>> t = MessageTemplate({'key 1': {'const': 'value'},
-... 'key 2': {'type': 'string'},
-... 'key 3': {'type': 'object',
-... 'properties': {
-... 'key 1': {'type': 'number'},
-... 'key 2': True,
-... 'key 3': False}}})
->>> t.check(Message('Example Sender',
-... {'key 1': 'value', 'key 2': 'some string'}))
-False
-
-
But for nested objects their properties do not necessarily have
-to be present:
-
>>> t.check(Message('Example Sender',
-... {'key 1': 'value', 'key 2': 'some string',
-... 'key 3': {'key 1': 42}}))
-True
-
-
Schema True matches everything (even None):
-
>>> t.check(Message('Example Sender',
-... {'key 1': 'value', 'key 2': 'some string',
-... 'key 3': {'key 2': None}}))
-True
-
-
Schema False matches nothing:
-
>>> t.check(Message('Example Sender',
-... {'key 1': 'value', 'key 2': 'some string',
-... 'key 3': {'key 3': True}}))
-False
-
-
Message is valid for the constant template created from it:
-
>>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
-... 'list': [None, True, 'string']})
->>> t = MessageTemplate.from_message(m)
->>> t.check(m)
-True
-
-
-
-Expand source code
-
-def check(self, message: Message) -> bool:
- """Check message against this template.
-
- Constant values have to match exactly:
- >>> t = MessageTemplate({'key': {'const': 'value'}})
- >>> t.check(Message('Example Sender', {'key': 'value'}))
- True
- >>> t.check(Message('Example Sender', {'key': 'other value'}))
- False
-
- But for integers, floats with the same value are also valid:
- >>> t = MessageTemplate({'key': {'const': 42}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.0}))
- True
-
- Type integer is valid for floats with zero fractional part, but
- not by floats with non-zero fractional part:
- >>> t = MessageTemplate({'key': {'type': 'integer'}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.0}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.42}))
- False
-
- Type number is valid for arbitrary ints or floats:
- >>> t = MessageTemplate({'key': {'type': 'number'}})
- >>> t.check(Message('Example Sender', {'key': 42}))
- True
- >>> t.check(Message('Example Sender', {'key': 42.42}))
- True
-
- All keys in template have to be present in message:
- >>> t = MessageTemplate({'key 1': {'const': 'value'},
- ... 'key 2': {'type': 'string'},
- ... 'key 3': {'type': 'object',
- ... 'properties': {
- ... 'key 1': {'type': 'number'},
- ... 'key 2': True,
- ... 'key 3': False}}})
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string'}))
- False
-
- But for nested objects their properties do not necessarily have
- to be present:
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 1': 42}}))
- True
-
- Schema True matches everything (even None):
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 2': None}}))
- True
-
- Schema False matches nothing:
- >>> t.check(Message('Example Sender',
- ... {'key 1': 'value', 'key 2': 'some string',
- ... 'key 3': {'key 3': True}}))
- False
-
- Message is valid for the constant template created from it:
- >>> m = Message('Example Sender', {'dict': {'int': 42, 'float': 42.42},
- ... 'list': [None, True, 'string']})
- >>> t = MessageTemplate.from_message(m)
- >>> t.check(m)
- True
- """
- for key in self:
- if key not in message:
- return False
- else:
- validator = jsonschema.Draft7Validator(self[key])
- for error in validator.iter_errors(message[key]):
- return False
- return True
-
-
-
-
-
-class MessageTemplateRegistry
-
-
-Manage a collection of message templates with registered clients.
-
A new MessageTemplateRegistry is created by:
-
>>> r = MessageTemplateRegistry()
-
-
Client names (strings) can be registered for message templates, which
-are mappings from keys to JSON schemas:
-
>>> r.insert({'k1': {'const': 'v1'}}, 'C 1')
-
-
The check function checks if the templates registered for a client
-match a given message:
-
>>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
-... print(f"{m}: {r.check('C 1', m)}")
-{'k1': 'v1', 'k2': 'v1'}: True
-{'k1': 'v1', 'k2': 2}: True
-{'k1': 'v2', 'k2': 'v1'}: False
-{'k1': 'v2', 'k2': 2}: False
-
-
Clients can be registered for values validating against arbitrary JSON
-schemas, e.g. all values of a certain type:
-
>>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}, 'C 2')
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
-... print(f"{m}: {r.check('C 2', m)}")
-{'k1': 'v1', 'k2': 'v1'}: False
-{'k1': 'v1', 'k2': 2}: False
-{'k1': 'v2', 'k2': 'v1'}: True
-{'k1': 'v2', 'k2': 2}: False
->>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}, 'C 3')
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
-... print(f"{m}: {r.check('C 3', m)}")
-{'k1': 'v1', 'k2': 'v1'}: False
-{'k1': 'v1', 'k2': 2}: False
-{'k1': 'v2', 'k2': 'v1'}: False
-{'k1': 'v2', 'k2': 2}: True
-
-
The order of key-value pairs does not have to match the order in the
-messages and keys can be left out:
-
>>> r.insert({'k2': {'const': 2}}, 'C 4')
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
-... print(f"{m}: {r.check('C 4', m)}")
-{'k1': 'v1', 'k2': 'v1'}: False
-{'k1': 'v1', 'k2': 2}: True
-{'k1': 'v2', 'k2': 'v1'}: False
-{'k1': 'v2', 'k2': 2}: True
-
-
A registration for an empty template matches all messages:
-
>>> r.insert({}, 'C 5')
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
-... print(f"{m}: {r.check('C 5', m)}")
-{'k1': 'v1', 'k2': 'v1'}: True
-{'k1': 'v1', 'k2': 2}: True
-{'k1': 'v2', 'k2': 'v1'}: True
-{'k1': 'v2', 'k2': 2}: True
-
-
A client can be registered for multiple templates:
-
>>> r.insert({'k1': {'const': 'v1'}}, 'C 6')
->>> r.insert({'k2': {'const': 'v1'}}, 'C 6')
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
-... print(f"{m}: {r.check('C 6', m)}")
-{'k1': 'v1', 'k2': 'v1'}: True
-{'k1': 'v1', 'k2': 2}: True
-{'k1': 'v2', 'k2': 'v1'}: True
-{'k1': 'v2', 'k2': 2}: False
-
-
Clients can be deregistered again (the result is False if the registry
-is empty after the deletion):
-
>>> r.insert({'k1': {'const': 'v1'}}, 'C 7')
->>> r.delete('C 7')
-True
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
-... print(f"{m}: {r.check('C 7', m)}")
-{'k1': 'v1', 'k2': 'v1'}: False
-{'k1': 'v1', 'k2': 2}: False
-{'k1': 'v2', 'k2': 'v1'}: False
-{'k1': 'v2', 'k2': 2}: False
-
-
The get function returns all clients with registered templates matching
-a given message:
-
>>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
-... print(f"{m}: {r.get(m)}")
-{'k1': 'v1', 'k2': 'v1'}: ['C 5', 'C 1', 'C 6']
-{'k1': 'v1', 'k2': 2}: ['C 5', 'C 1', 'C 6', 'C 4']
-{'k1': 'v2', 'k2': 'v1'}: ['C 5', 'C 2', 'C 6']
-{'k1': 'v2', 'k2': 2}: ['C 5', 'C 3', 'C 4']
-
-
The get_templates function returns all templates for a given client:
-
>>> for c in ['C 1', 'C 2', 'C 3', 'C 4', 'C 5', 'C 6']:
-... print(f"{c}: {r.get_templates(c)}")
-C 1: [{'k1': {'const': 'v1'}}]
-C 2: [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
-C 3: [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
-C 4: [{'k2': {'const': 2}}]
-C 5: [{}]
-C 6: [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
-
-
Initialise an empty registry.
-
>>> r = MessageTemplateRegistry()
-
-
-
-Expand source code
-
-class MessageTemplateRegistry:
- """Manage a collection of message templates with registered clients.
-
- A new MessageTemplateRegistry is created by:
- >>> r = MessageTemplateRegistry()
-
- Client names (strings) can be registered for message templates, which
- are mappings from keys to JSON schemas:
- >>> r.insert({'k1': {'const': 'v1'}}, 'C 1')
-
- The check function checks if the templates registered for a client
- match a given message:
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 1', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 2}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 2}: False
-
- Clients can be registered for values validating against arbitrary JSON
- schemas, e.g. all values of a certain type:
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}, 'C 2')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 2', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 2}: False
- {'k1': 'v2', 'k2': 'v1'}: True
- {'k1': 'v2', 'k2': 2}: False
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}, 'C 3')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 3', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 2}: False
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 2}: True
-
- The order of key-value pairs does not have to match the order in the
- messages and keys can be left out:
- >>> r.insert({'k2': {'const': 2}}, 'C 4')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 4', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 2}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 2}: True
-
- A registration for an empty template matches all messages:
- >>> r.insert({}, 'C 5')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 5', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 2}: True
- {'k1': 'v2', 'k2': 'v1'}: True
- {'k1': 'v2', 'k2': 2}: True
-
- A client can be registered for multiple templates:
- >>> r.insert({'k1': {'const': 'v1'}}, 'C 6')
- >>> r.insert({'k2': {'const': 'v1'}}, 'C 6')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 6', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 2}: True
- {'k1': 'v2', 'k2': 'v1'}: True
- {'k1': 'v2', 'k2': 2}: False
-
- Clients can be deregistered again (the result is False if the registry
- is empty after the deletion):
- >>> r.insert({'k1': {'const': 'v1'}}, 'C 7')
- >>> r.delete('C 7')
- True
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.check('C 7', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 2}: False
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 2}: False
-
- The get function returns all clients with registered templates matching
- a given message:
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 2},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 2}]:
- ... print(f"{m}: {r.get(m)}")
- {'k1': 'v1', 'k2': 'v1'}: ['C 5', 'C 1', 'C 6']
- {'k1': 'v1', 'k2': 2}: ['C 5', 'C 1', 'C 6', 'C 4']
- {'k1': 'v2', 'k2': 'v1'}: ['C 5', 'C 2', 'C 6']
- {'k1': 'v2', 'k2': 2}: ['C 5', 'C 3', 'C 4']
-
- The get_templates function returns all templates for a given client:
- >>> for c in ['C 1', 'C 2', 'C 3', 'C 4', 'C 5', 'C 6']:
- ... print(f"{c}: {r.get_templates(c)}")
- C 1: [{'k1': {'const': 'v1'}}]
- C 2: [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
- C 3: [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
- C 4: [{'k2': {'const': 2}}]
- C 5: [{}]
- C 6: [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
- """
-
- def __init__(self) -> None:
- """Initialise an empty registry.
-
- >>> r = MessageTemplateRegistry()
- """
- self._clients: List[str] = []
- self._children: Dict[str, Dict[str, MessageTemplateRegistry]] = {}
-
- def insert(self, template: MessageTemplate, client: str) -> None:
- """Register a client for a template.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v1'}}, 'C 1')
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v2'}}, 'C 2')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v1'}}, 'C 3')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v2'}}, 'C 4')
- >>> r.insert({}, 'C 5')
-
- Implementation details:
- -----------------------
- The tree nodes on the way to a registered object are used/created
- in the order given in the message template, which can be used to
- design more efficient lookups (e.g., putting rarer key-value pairs
- earlier in the template).
- >>> r._clients
- ['C 5']
- >>> r._children.keys()
- dict_keys(['k1'])
- >>> r._children['k1'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> r._children['k1']['{"const": "v1"}']._clients
- []
- >>> r._children['k1']['{"const": "v1"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v1"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 1']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 2']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- >>> r._children['k1']['{"const": "v2"}']._clients
- []
- >>> r._children['k1']['{"const": "v2"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v2"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 3']
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 4']
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- """
- if not template:
- self._clients.append(client)
- else:
- key, schema = next(iter(template.items()))
- schema_string = json.dumps(schema)
- reduced_template = MessageTemplate({k: template[k]
- for k in template
- if k != key})
- if key not in self._children:
- self._children[key] = {}
- if schema_string not in self._children[key]:
- self._children[key][schema_string] = MessageTemplateRegistry()
- self._children[key][schema_string].insert(reduced_template, client)
-
- def delete(self, client: str) -> bool:
- """Unregister a client from all templates.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v1'}}, 'C 1')
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v2'}}, 'C 2')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v1'}}, 'C 3')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v2'}}, 'C 4')
- >>> r.insert({}, 'C 5')
- >>> r.delete('C 3')
- True
- >>> r.delete('C 4')
- True
-
- Implementation details:
- -----------------------
- If parts of the tree become superfluous by the deletion of the
- client, they are also completely removed to reduce the lookup
- effort and keep the tree clean.
- >>> r._clients
- ['C 5']
- >>> r._children.keys()
- dict_keys(['k1'])
- >>> r._children['k1'].keys()
- dict_keys(['{"const": "v1"}'])
- >>> r._children['k1']['{"const": "v1"}']._clients
- []
- >>> r._children['k1']['{"const": "v1"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v1"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 1']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 2']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- """
- self._clients = [c for c in self._clients if c != client]
- new_children: Dict[str, Dict[str, MessageTemplateRegistry]] = {}
- for key in self._children:
- new_children[key] = {}
- for schema in self._children[key]:
- if self._children[key][schema].delete(client):
- new_children[key][schema] = self._children[key][schema]
- if not new_children[key]:
- del new_children[key]
- self._children = new_children
- if self._clients or self._children:
- return True
- return False
-
- def check(self, client: str, message: Message) -> bool:
- """Get if a client has a registered template matching a message.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.check('Client 1', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 'v2'}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 'v2'}: False
- >>> r.insert({'k2': {'const': 'v2'}}, 'Client 2')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.check('Client 2', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 'v2'}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 'v2'}: True
- """
- if client in self._clients:
- return True
- for key in self._children:
- if key in message:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- validator = jsonschema.Draft7Validator(schema)
- validated = True
- for error in validator.iter_errors(message[key]):
- validated = False
- if validated:
- child = self._children[key][schema_string]
- if child.check(client, message):
- return True
- return False
-
- def get(self, message: Message) -> List[str]:
- """Get all clients registered for templates matching a message.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> r.insert({'k2': {'const': 'v2'}}, 'Client 2')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.get(m)}")
- {'k1': 'v1', 'k2': 'v1'}: ['Client 1']
- {'k1': 'v1', 'k2': 'v2'}: ['Client 1', 'Client 2']
- {'k1': 'v2', 'k2': 'v1'}: []
- {'k1': 'v2', 'k2': 'v2'}: ['Client 2']
- """
- result = []
- for client in self._clients:
- if client not in result:
- result.append(client)
- for key in self._children:
- if key in message:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- validator = jsonschema.Draft7Validator(schema)
- validated = True
- for error in validator.iter_errors(message[key]):
- validated = False
- if validated:
- child = self._children[key][schema_string]
- for client in child.get(message):
- if client not in result:
- result.append(client)
- return result
-
- def get_templates(self, client: str) -> List[MessageTemplate]:
- """Get all templates for a client.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> r.get_templates('Client 1')
- [{'k1': {'const': 'v1'}}]
- >>> r.insert({'k1': {'const': 'v2'},
- ... 'k2': {'type': 'string'}}, 'Client 2')
- >>> r.get_templates('Client 2')
- [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
- >>> r.insert({'k1': {'const': 'v2'},
- ... 'k2': {'type': 'integer'}}, 'Client 3')
- >>> r.get_templates('Client 3')
- [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
- >>> r.insert({'k2': {'const': 2}}, 'Client 4')
- >>> r.get_templates('Client 4')
- [{'k2': {'const': 2}}]
- >>> r.insert({}, 'Client 5')
- >>> r.get_templates('Client 5')
- [{}]
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 6')
- >>> r.insert({'k2': {'const': 'v1'}}, 'Client 6')
- >>> r.get_templates('Client 6')
- [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
- """
- result = []
- if client in self._clients:
- result.append(MessageTemplate())
- for key in self._children:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- child = self._children[key][schema_string]
- for template in child.get_templates(client):
- current = MessageTemplate({key: schema})
- current.update(template)
- result.append(current)
- return result
-
-Methods
-
-
-def insert (self, template:Â MessageTemplate , client:Â str) â>Â NoneType
-
-
-Register a client for a template.
-
>>> r = MessageTemplateRegistry()
->>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v1'}}, 'C 1')
->>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v2'}}, 'C 2')
->>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v1'}}, 'C 3')
->>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v2'}}, 'C 4')
->>> r.insert({}, 'C 5')
-
-
Implementation details:
-
The tree nodes on the way to a registered object are used/created
-in the order given in the message template, which can be used to
-design more efficient lookups (e.g., putting rarer key-value pairs
-earlier in the template).
-
>>> r._clients
-['C 5']
->>> r._children.keys()
-dict_keys(['k1'])
->>> r._children['k1'].keys()
-dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
->>> r._children['k1']['{"const": "v1"}']._clients
-[]
->>> r._children['k1']['{"const": "v1"}']._children.keys()
-dict_keys(['k2'])
->>> r._children['k1']['{"const": "v1"}']._children['k2'].keys()
-dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
->>> (r._children['k1']['{"const": "v1"}']
-... ._children['k2']['{"const": "v1"}'])._clients
-['C 1']
->>> (r._children['k1']['{"const": "v1"}']
-... ._children['k2']['{"const": "v1"}'])._children.keys()
-dict_keys([])
->>> (r._children['k1']['{"const": "v1"}']
-... ._children['k2']['{"const": "v2"}'])._clients
-['C 2']
->>> (r._children['k1']['{"const": "v1"}']
-... ._children['k2']['{"const": "v2"}'])._children.keys()
-dict_keys([])
->>> r._children['k1']['{"const": "v2"}']._clients
-[]
->>> r._children['k1']['{"const": "v2"}']._children.keys()
-dict_keys(['k2'])
->>> r._children['k1']['{"const": "v2"}']._children['k2'].keys()
-dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
->>> (r._children['k1']['{"const": "v2"}']
-... ._children['k2']['{"const": "v1"}'])._clients
-['C 3']
->>> (r._children['k1']['{"const": "v2"}']
-... ._children['k2']['{"const": "v1"}'])._children.keys()
-dict_keys([])
->>> (r._children['k1']['{"const": "v2"}']
-... ._children['k2']['{"const": "v2"}'])._clients
-['C 4']
->>> (r._children['k1']['{"const": "v2"}']
-... ._children['k2']['{"const": "v2"}'])._children.keys()
-dict_keys([])
-
-
-
-Expand source code
-
-def insert(self, template: MessageTemplate, client: str) -> None:
- """Register a client for a template.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v1'}}, 'C 1')
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v2'}}, 'C 2')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v1'}}, 'C 3')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v2'}}, 'C 4')
- >>> r.insert({}, 'C 5')
-
- Implementation details:
- -----------------------
- The tree nodes on the way to a registered object are used/created
- in the order given in the message template, which can be used to
- design more efficient lookups (e.g., putting rarer key-value pairs
- earlier in the template).
- >>> r._clients
- ['C 5']
- >>> r._children.keys()
- dict_keys(['k1'])
- >>> r._children['k1'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> r._children['k1']['{"const": "v1"}']._clients
- []
- >>> r._children['k1']['{"const": "v1"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v1"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 1']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 2']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- >>> r._children['k1']['{"const": "v2"}']._clients
- []
- >>> r._children['k1']['{"const": "v2"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v2"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 3']
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 4']
- >>> (r._children['k1']['{"const": "v2"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- """
- if not template:
- self._clients.append(client)
- else:
- key, schema = next(iter(template.items()))
- schema_string = json.dumps(schema)
- reduced_template = MessageTemplate({k: template[k]
- for k in template
- if k != key})
- if key not in self._children:
- self._children[key] = {}
- if schema_string not in self._children[key]:
- self._children[key][schema_string] = MessageTemplateRegistry()
- self._children[key][schema_string].insert(reduced_template, client)
-
-
-
-def delete (self, client:Â str) â>Â bool
-
-
-Unregister a client from all templates.
-
>>> r = MessageTemplateRegistry()
->>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v1'}}, 'C 1')
->>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v2'}}, 'C 2')
->>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v1'}}, 'C 3')
->>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v2'}}, 'C 4')
->>> r.insert({}, 'C 5')
->>> r.delete('C 3')
-True
->>> r.delete('C 4')
-True
-
-
Implementation details:
-
If parts of the tree become superfluous by the deletion of the
-client, they are also completely removed to reduce the lookup
-effort and keep the tree clean.
-
>>> r._clients
-['C 5']
->>> r._children.keys()
-dict_keys(['k1'])
->>> r._children['k1'].keys()
-dict_keys(['{"const": "v1"}'])
->>> r._children['k1']['{"const": "v1"}']._clients
-[]
->>> r._children['k1']['{"const": "v1"}']._children.keys()
-dict_keys(['k2'])
->>> r._children['k1']['{"const": "v1"}']._children['k2'].keys()
-dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
->>> (r._children['k1']['{"const": "v1"}']
-... ._children['k2']['{"const": "v1"}'])._clients
-['C 1']
->>> (r._children['k1']['{"const": "v1"}']
-... ._children['k2']['{"const": "v1"}'])._children.keys()
-dict_keys([])
->>> (r._children['k1']['{"const": "v1"}']
-... ._children['k2']['{"const": "v2"}'])._clients
-['C 2']
->>> (r._children['k1']['{"const": "v1"}']
-... ._children['k2']['{"const": "v2"}'])._children.keys()
-dict_keys([])
-
-
-
-Expand source code
-
-def delete(self, client: str) -> bool:
- """Unregister a client from all templates.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v1'}}, 'C 1')
- >>> r.insert({'k1': {'const': 'v1'}, 'k2': {'const': 'v2'}}, 'C 2')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v1'}}, 'C 3')
- >>> r.insert({'k1': {'const': 'v2'}, 'k2': {'const': 'v2'}}, 'C 4')
- >>> r.insert({}, 'C 5')
- >>> r.delete('C 3')
- True
- >>> r.delete('C 4')
- True
-
- Implementation details:
- -----------------------
- If parts of the tree become superfluous by the deletion of the
- client, they are also completely removed to reduce the lookup
- effort and keep the tree clean.
- >>> r._clients
- ['C 5']
- >>> r._children.keys()
- dict_keys(['k1'])
- >>> r._children['k1'].keys()
- dict_keys(['{"const": "v1"}'])
- >>> r._children['k1']['{"const": "v1"}']._clients
- []
- >>> r._children['k1']['{"const": "v1"}']._children.keys()
- dict_keys(['k2'])
- >>> r._children['k1']['{"const": "v1"}']._children['k2'].keys()
- dict_keys(['{"const": "v1"}', '{"const": "v2"}'])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._clients
- ['C 1']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v1"}'])._children.keys()
- dict_keys([])
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._clients
- ['C 2']
- >>> (r._children['k1']['{"const": "v1"}']
- ... ._children['k2']['{"const": "v2"}'])._children.keys()
- dict_keys([])
- """
- self._clients = [c for c in self._clients if c != client]
- new_children: Dict[str, Dict[str, MessageTemplateRegistry]] = {}
- for key in self._children:
- new_children[key] = {}
- for schema in self._children[key]:
- if self._children[key][schema].delete(client):
- new_children[key][schema] = self._children[key][schema]
- if not new_children[key]:
- del new_children[key]
- self._children = new_children
- if self._clients or self._children:
- return True
- return False
-
-
-
-def check (self, client:Â str, message:Â Message ) â>Â bool
-
-
-Get if a client has a registered template matching a message.
-
>>> r = MessageTemplateRegistry()
->>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
-... print(f"{m}: {r.check('Client 1', m)}")
-{'k1': 'v1', 'k2': 'v1'}: True
-{'k1': 'v1', 'k2': 'v2'}: True
-{'k1': 'v2', 'k2': 'v1'}: False
-{'k1': 'v2', 'k2': 'v2'}: False
->>> r.insert({'k2': {'const': 'v2'}}, 'Client 2')
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
-... print(f"{m}: {r.check('Client 2', m)}")
-{'k1': 'v1', 'k2': 'v1'}: False
-{'k1': 'v1', 'k2': 'v2'}: True
-{'k1': 'v2', 'k2': 'v1'}: False
-{'k1': 'v2', 'k2': 'v2'}: True
-
-
-
-Expand source code
-
-def check(self, client: str, message: Message) -> bool:
- """Get if a client has a registered template matching a message.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.check('Client 1', m)}")
- {'k1': 'v1', 'k2': 'v1'}: True
- {'k1': 'v1', 'k2': 'v2'}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 'v2'}: False
- >>> r.insert({'k2': {'const': 'v2'}}, 'Client 2')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.check('Client 2', m)}")
- {'k1': 'v1', 'k2': 'v1'}: False
- {'k1': 'v1', 'k2': 'v2'}: True
- {'k1': 'v2', 'k2': 'v1'}: False
- {'k1': 'v2', 'k2': 'v2'}: True
- """
- if client in self._clients:
- return True
- for key in self._children:
- if key in message:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- validator = jsonschema.Draft7Validator(schema)
- validated = True
- for error in validator.iter_errors(message[key]):
- validated = False
- if validated:
- child = self._children[key][schema_string]
- if child.check(client, message):
- return True
- return False
-
-
-
-def get (self, message:Â Message ) â>Â List[str]
-
-
-Get all clients registered for templates matching a message.
-
>>> r = MessageTemplateRegistry()
->>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
->>> r.insert({'k2': {'const': 'v2'}}, 'Client 2')
->>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
-... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
-... print(f"{m}: {r.get(m)}")
-{'k1': 'v1', 'k2': 'v1'}: ['Client 1']
-{'k1': 'v1', 'k2': 'v2'}: ['Client 1', 'Client 2']
-{'k1': 'v2', 'k2': 'v1'}: []
-{'k1': 'v2', 'k2': 'v2'}: ['Client 2']
-
-
-
-Expand source code
-
-def get(self, message: Message) -> List[str]:
- """Get all clients registered for templates matching a message.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> r.insert({'k2': {'const': 'v2'}}, 'Client 2')
- >>> for m in [{'k1': 'v1', 'k2': 'v1'}, {'k1': 'v1', 'k2': 'v2'},
- ... {'k1': 'v2', 'k2': 'v1'}, {'k1': 'v2', 'k2': 'v2'}]:
- ... print(f"{m}: {r.get(m)}")
- {'k1': 'v1', 'k2': 'v1'}: ['Client 1']
- {'k1': 'v1', 'k2': 'v2'}: ['Client 1', 'Client 2']
- {'k1': 'v2', 'k2': 'v1'}: []
- {'k1': 'v2', 'k2': 'v2'}: ['Client 2']
- """
- result = []
- for client in self._clients:
- if client not in result:
- result.append(client)
- for key in self._children:
- if key in message:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- validator = jsonschema.Draft7Validator(schema)
- validated = True
- for error in validator.iter_errors(message[key]):
- validated = False
- if validated:
- child = self._children[key][schema_string]
- for client in child.get(message):
- if client not in result:
- result.append(client)
- return result
-
-
-
-def get_templates (self, client:Â str) â>Â List[MessageTemplate ]
-
-
-Get all templates for a client.
-
>>> r = MessageTemplateRegistry()
->>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
->>> r.get_templates('Client 1')
-[{'k1': {'const': 'v1'}}]
->>> r.insert({'k1': {'const': 'v2'},
-... 'k2': {'type': 'string'}}, 'Client 2')
->>> r.get_templates('Client 2')
-[{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
->>> r.insert({'k1': {'const': 'v2'},
-... 'k2': {'type': 'integer'}}, 'Client 3')
->>> r.get_templates('Client 3')
-[{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
->>> r.insert({'k2': {'const': 2}}, 'Client 4')
->>> r.get_templates('Client 4')
-[{'k2': {'const': 2}}]
->>> r.insert({}, 'Client 5')
->>> r.get_templates('Client 5')
-[{}]
->>> r.insert({'k1': {'const': 'v1'}}, 'Client 6')
->>> r.insert({'k2': {'const': 'v1'}}, 'Client 6')
->>> r.get_templates('Client 6')
-[{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
-
-
-
-Expand source code
-
-def get_templates(self, client: str) -> List[MessageTemplate]:
- """Get all templates for a client.
-
- >>> r = MessageTemplateRegistry()
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 1')
- >>> r.get_templates('Client 1')
- [{'k1': {'const': 'v1'}}]
- >>> r.insert({'k1': {'const': 'v2'},
- ... 'k2': {'type': 'string'}}, 'Client 2')
- >>> r.get_templates('Client 2')
- [{'k1': {'const': 'v2'}, 'k2': {'type': 'string'}}]
- >>> r.insert({'k1': {'const': 'v2'},
- ... 'k2': {'type': 'integer'}}, 'Client 3')
- >>> r.get_templates('Client 3')
- [{'k1': {'const': 'v2'}, 'k2': {'type': 'integer'}}]
- >>> r.insert({'k2': {'const': 2}}, 'Client 4')
- >>> r.get_templates('Client 4')
- [{'k2': {'const': 2}}]
- >>> r.insert({}, 'Client 5')
- >>> r.get_templates('Client 5')
- [{}]
- >>> r.insert({'k1': {'const': 'v1'}}, 'Client 6')
- >>> r.insert({'k2': {'const': 'v1'}}, 'Client 6')
- >>> r.get_templates('Client 6')
- [{'k1': {'const': 'v1'}}, {'k2': {'const': 'v1'}}]
- """
- result = []
- if client in self._clients:
- result.append(MessageTemplate())
- for key in self._children:
- for schema_string in self._children[key]:
- schema = json.loads(schema_string)
- child = self._children[key][schema_string]
- for template in child.get_templates(client):
- current = MessageTemplate({key: schema})
- current.update(template)
- result.append(current)
- return result
-
-
-
-
-
-class BusException
-( *args, **kwargs)
-
-
-Raise for errors in using message bus.
-
-
-Expand source code
-
-class BusException(Exception):
- """Raise for errors in using message bus."""
-
-Ancestors
-
-builtins.Exception
-builtins.BaseException
-
-
-
-class MessageBus
-
-
-Provide an asynchronous message bus.
-
The bus executes asynchronous callbacks for all messages to be received
-by a client. We use a simple callback printing the message in all
-examples:
-
>>> def callback_for_receiver(receiver):
-... print(f"Creating callback for {receiver}.")
-... async def callback(message):
-... print(f"{receiver}: {message}")
-... return callback
-
-
Clients can be registered at the bus with a name, lists of message
-templates they want to use for sending and receiving and a callback
-function for receiving. An empty list of templates means that the
-client does not want to send or receive any messages, respectively.
-A list with an empty template means that it wants to send arbitrary
-or receive all messages, respectively:
-
>>> async def setup(bus):
-... print("Setting up.")
-... bus.register('Logger', 'Test Plugin',
-... [],
-... [{}],
-... callback_for_receiver('Logger'))
-... bus.register('Client 1', 'Test Plugin',
-... [{'k1': {'type': 'string'}}],
-... [{'target': {'const': 'Client 1'}}],
-... callback_for_receiver('Client 1'))
-... bus.register('Client 2', 'Test Plugin',
-... [{}],
-... [{'target': {'const': 'Client 2'}}],
-... callback_for_receiver('Client 2'))
-
-
The bus itself is addressed by the empty string. It sends messages for
-each registration and deregestration of a client with a key 'event' and
-a value of 'registered' or 'unregistered', a key 'client' with the
-client's name as value and for registrations also keys 'sends' and
-'receives' with all templates registered for the client for sending and
-receiving.
-
Clients can send to the bus with the send function. Each message has to
-declare a sender. The send templates of that sender are checked for a
-template matching the message. We cannot prevent arbitrary code from
-impersonating any sender, but this should only be done in debugging or
-management situations.
-
Messages that are intended for a specific client by convention have a
-key 'target' with the target client's name as value. Such messages are
-often commands to the client to do something, which is by convention
-indicated by a key 'command' with a value that indicates what should be
-done.
-
The bus, for example, reacts to a message with 'target': '' and
-'command': 'get clients' by sending one message for each currently
-registered with complete information about its registered send and
-receive templates.
-
>>> async def send(bus):
-... print("Sending messages.")
-... await bus.send({'sender': 'Client 1', 'k1': 'Test'})
-... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
-... await bus.send({'sender': '', 'target': '',
-... 'command': 'get clients'})
-
-
The run function executes the message bus forever. If we want to stop
-it, we have to explicitly cancel the task:
-
>>> async def main():
-... bus = MessageBus()
-... await setup(bus)
-... bus_task = asyncio.create_task(bus.run())
-... await send(bus)
-... await asyncio.sleep(0)
-... bus_task.cancel()
->>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
-Setting up.
-Creating callback for Logger.
-Creating callback for Client 1.
-Creating callback for Client 2.
-Sending messages.
-Logger: {'sender': '', 'event': 'registered',
- 'client': 'Logger', 'plugin': 'Test Plugin',
- 'sends': [], 'receives': [{}]}
-Logger: {'sender': '', 'event': 'registered',
- 'client': 'Client 1', 'plugin': 'Test Plugin',
- 'sends': [{'k1': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Client 1'}}]}
-Logger: {'sender': '', 'event': 'registered',
- 'client': 'Client 2', 'plugin': 'Test Plugin',
- 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
-Logger: {'sender': 'Client 1', 'k1': 'Test'}
-Logger: {'sender': 'Client 2', 'target': 'Client 1'}
-Client 1: {'sender': 'Client 2', 'target': 'Client 1'}
-Logger: {'sender': '', 'target': '', 'command': 'get clients'}
-Logger: {'sender': '', 'client': 'Logger', 'plugin': 'Test Plugin',
- 'sends': [], 'receives': [{}]}
-Logger: {'sender': '', 'client': 'Client 1', 'plugin': 'Test Plugin',
- 'sends': [{'k1': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Client 1'}}]}
-Logger: {'sender': '', 'client': 'Client 2', 'plugin': 'Test Plugin',
- 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
-
-
Initialise a new bus without clients.
-
>>> async def main():
-... bus = MessageBus()
->>> asyncio.run(main())
-
-
-
-Expand source code
-
-class MessageBus:
- """Provide an asynchronous message bus.
-
- The bus executes asynchronous callbacks for all messages to be received
- by a client. We use a simple callback printing the message in all
- examples:
- >>> def callback_for_receiver(receiver):
- ... print(f"Creating callback for {receiver}.")
- ... async def callback(message):
- ... print(f"{receiver}: {message}")
- ... return callback
-
- Clients can be registered at the bus with a name, lists of message
- templates they want to use for sending and receiving and a callback
- function for receiving. An empty list of templates means that the
- client does not want to send or receive any messages, respectively.
- A list with an empty template means that it wants to send arbitrary
- or receive all messages, respectively:
- >>> async def setup(bus):
- ... print("Setting up.")
- ... bus.register('Logger', 'Test Plugin',
- ... [],
- ... [{}],
- ... callback_for_receiver('Logger'))
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... [{'target': {'const': 'Client 1'}}],
- ... callback_for_receiver('Client 1'))
- ... bus.register('Client 2', 'Test Plugin',
- ... [{}],
- ... [{'target': {'const': 'Client 2'}}],
- ... callback_for_receiver('Client 2'))
-
- The bus itself is addressed by the empty string. It sends messages for
- each registration and deregestration of a client with a key 'event' and
- a value of 'registered' or 'unregistered', a key 'client' with the
- client's name as value and for registrations also keys 'sends' and
- 'receives' with all templates registered for the client for sending and
- receiving.
-
- Clients can send to the bus with the send function. Each message has to
- declare a sender. The send templates of that sender are checked for a
- template matching the message. We cannot prevent arbitrary code from
- impersonating any sender, but this should only be done in debugging or
- management situations.
-
- Messages that are intended for a specific client by convention have a
- key 'target' with the target client's name as value. Such messages are
- often commands to the client to do something, which is by convention
- indicated by a key 'command' with a value that indicates what should be
- done.
-
- The bus, for example, reacts to a message with 'target': '' and
- 'command': 'get clients' by sending one message for each currently
- registered with complete information about its registered send and
- receive templates.
-
- >>> async def send(bus):
- ... print("Sending messages.")
- ... await bus.send({'sender': 'Client 1', 'k1': 'Test'})
- ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
- ... await bus.send({'sender': '', 'target': '',
- ... 'command': 'get clients'})
-
- The run function executes the message bus forever. If we want to stop
- it, we have to explicitly cancel the task:
- >>> async def main():
- ... bus = MessageBus()
- ... await setup(bus)
- ... bus_task = asyncio.create_task(bus.run())
- ... await send(bus)
- ... await asyncio.sleep(0)
- ... bus_task.cancel()
- >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
- Setting up.
- Creating callback for Logger.
- Creating callback for Client 1.
- Creating callback for Client 2.
- Sending messages.
- Logger: {'sender': '', 'event': 'registered',
- 'client': 'Logger', 'plugin': 'Test Plugin',
- 'sends': [], 'receives': [{}]}
- Logger: {'sender': '', 'event': 'registered',
- 'client': 'Client 1', 'plugin': 'Test Plugin',
- 'sends': [{'k1': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Client 1'}}]}
- Logger: {'sender': '', 'event': 'registered',
- 'client': 'Client 2', 'plugin': 'Test Plugin',
- 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
- Logger: {'sender': 'Client 1', 'k1': 'Test'}
- Logger: {'sender': 'Client 2', 'target': 'Client 1'}
- Client 1: {'sender': 'Client 2', 'target': 'Client 1'}
- Logger: {'sender': '', 'target': '', 'command': 'get clients'}
- Logger: {'sender': '', 'client': 'Logger', 'plugin': 'Test Plugin',
- 'sends': [], 'receives': [{}]}
- Logger: {'sender': '', 'client': 'Client 1', 'plugin': 'Test Plugin',
- 'sends': [{'k1': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Client 1'}}]}
- Logger: {'sender': '', 'client': 'Client 2', 'plugin': 'Test Plugin',
- 'sends': [{}], 'receives': [{'target': {'const': 'Client 2'}}]}
- """
-
- def __init__(self) -> None:
- """Initialise a new bus without clients.
-
- >>> async def main():
- ... bus = MessageBus()
- >>> asyncio.run(main())
- """
- self._queue: asyncio.Queue = asyncio.Queue()
- self._plugins: Dict[str, str] = {}
- self._send_reg: MessageTemplateRegistry = MessageTemplateRegistry()
- self._recv_reg: MessageTemplateRegistry = MessageTemplateRegistry()
- self._callbacks: Dict[str, MessageCallback] = {}
-
- def register(self, client: str, plugin: str,
- sends: Iterable[MessageTemplate],
- receives: Iterable[MessageTemplate],
- callback: MessageCallback) -> None:
- """Register a client at the message bus.
-
- >>> async def callback(message):
- ... print(message)
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Logger', 'Test Plugin',
- ... [], # send nothing
- ... [{}], # receive everything
- ... callback)
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... # send with key 'k1' and string value
- ... [{'target': {'const': 'Client 1'}}],
- ... # receive for this client
- ... callback)
- ... bus.register('Client 2', 'Test Plugin',
- ... [{}], # send arbitrary
- ... [{'target': {'const': 'Client 2'}}],
- ... # receive for this client
- ... callback)
- >>> asyncio.run(main())
- """
- if not client:
- raise BusException("Client name is not allowed to be empty.")
- if client in self._plugins:
- raise BusException(f"Client '{client}' already registered"
- " at message bus.")
- event = Message('')
- event['event'] = 'registered'
- event['client'] = client
- self._plugins[client] = plugin
- event['plugin'] = plugin
- for template in sends:
- self._send_reg.insert(template, client)
- event['sends'] = self._send_reg.get_templates(client)
- for template in receives:
- self._recv_reg.insert(template, client)
- event['receives'] = self._recv_reg.get_templates(client)
- self._callbacks[client] = callback
- self._queue.put_nowait(event)
-
- def unregister(self, client: str) -> None:
- """Unregister a client from the message bus.
-
- >>> async def callback(message):
- ... print(message)
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... [{'target': {'const': 'Client 1'}}],
- ... callback)
- ... bus.unregister('Client 1')
- >>> asyncio.run(main())
- """
- if client not in self._plugins:
- return
- event = Message('')
- event['event'] = 'unregistered'
- event['client'] = client
- del self._plugins[client]
- self._send_reg.delete(client)
- self._recv_reg.delete(client)
- if client in self._callbacks:
- del self._callbacks[client]
- self._queue.put_nowait(event)
-
- async def run(self) -> None:
- """Run the message bus forever.
-
- >>> async def main():
- ... bus = MessageBus()
- ... bus_task = asyncio.create_task(bus.run())
- ... bus_task.cancel()
- >>> asyncio.run(main())
- """
- while True:
- message = await self._queue.get()
- if ('target' in message and
- message['target'] == '' and
- 'command' in message and
- message['command'] == 'get clients'):
- for client in self._plugins:
- answer = Message('')
- answer['client'] = client
- answer['plugin'] = self._plugins[client]
- answer['sends'] = self._send_reg.get_templates(client)
- answer['receives'] = self._recv_reg.get_templates(client)
- await self._queue.put(answer)
- for client in self._recv_reg.get(message):
- await self._callbacks[client](message)
- self._queue.task_done()
-
- async def send(self, message: Message) -> None:
- """Send a message to the message bus.
-
- >>> async def callback(message):
- ... print(f"Got: {message}")
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... [{'target': {'const': 'Client 1'}}],
- ... callback)
- ... bus.register('Client 2', 'Test Plugin',
- ... [{}],
- ... [{'target': {'const': 'Client 2'}}],
- ... callback)
- ... bus_task = asyncio.create_task(bus.run())
- ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
- ... 'k1': 'Test'})
- ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
- ... try:
- ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
- ... 'k1': 42})
- ... except BusException as e:
- ... print(e)
- ... await asyncio.sleep(0)
- ... bus_task.cancel()
- >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
- Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
- not allowed for sender 'Client 1'.
- Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
- Got: {'sender': 'Client 2', 'target': 'Client 1'}
- """
- assert isinstance(message['sender'], str)
- sender = message['sender']
- if sender:
- if not self._send_reg.check(sender, message):
- raise BusException(f"Message '{message}' not allowed for"
- f" sender '{sender}'.")
- await self._queue.put(message)
-
-Methods
-
-
-def register (self, client: str, plugin: str, sends: Iterable[MessageTemplate ], receives: Iterable[MessageTemplate ], callback: Callable[[ForwardRef('Message ')], Coroutine[Any, Any, NoneType]]) â> NoneType
-
-
-Register a client at the message bus.
-
>>> async def callback(message):
-... print(message)
->>> async def main():
-... bus = MessageBus()
-... bus.register('Logger', 'Test Plugin',
-... [], # send nothing
-... [{}], # receive everything
-... callback)
-... bus.register('Client 1', 'Test Plugin',
-... [{'k1': {'type': 'string'}}],
-... # send with key 'k1' and string value
-... [{'target': {'const': 'Client 1'}}],
-... # receive for this client
-... callback)
-... bus.register('Client 2', 'Test Plugin',
-... [{}], # send arbitrary
-... [{'target': {'const': 'Client 2'}}],
-... # receive for this client
-... callback)
->>> asyncio.run(main())
-
-
-
-Expand source code
-
-def register(self, client: str, plugin: str,
- sends: Iterable[MessageTemplate],
- receives: Iterable[MessageTemplate],
- callback: MessageCallback) -> None:
- """Register a client at the message bus.
-
- >>> async def callback(message):
- ... print(message)
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Logger', 'Test Plugin',
- ... [], # send nothing
- ... [{}], # receive everything
- ... callback)
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... # send with key 'k1' and string value
- ... [{'target': {'const': 'Client 1'}}],
- ... # receive for this client
- ... callback)
- ... bus.register('Client 2', 'Test Plugin',
- ... [{}], # send arbitrary
- ... [{'target': {'const': 'Client 2'}}],
- ... # receive for this client
- ... callback)
- >>> asyncio.run(main())
- """
- if not client:
- raise BusException("Client name is not allowed to be empty.")
- if client in self._plugins:
- raise BusException(f"Client '{client}' already registered"
- " at message bus.")
- event = Message('')
- event['event'] = 'registered'
- event['client'] = client
- self._plugins[client] = plugin
- event['plugin'] = plugin
- for template in sends:
- self._send_reg.insert(template, client)
- event['sends'] = self._send_reg.get_templates(client)
- for template in receives:
- self._recv_reg.insert(template, client)
- event['receives'] = self._recv_reg.get_templates(client)
- self._callbacks[client] = callback
- self._queue.put_nowait(event)
-
-
-
-def unregister (self, client:Â str) â>Â NoneType
-
-
-Unregister a client from the message bus.
-
>>> async def callback(message):
-... print(message)
->>> async def main():
-... bus = MessageBus()
-... bus.register('Client 1', 'Test Plugin',
-... [{'k1': {'type': 'string'}}],
-... [{'target': {'const': 'Client 1'}}],
-... callback)
-... bus.unregister('Client 1')
->>> asyncio.run(main())
-
-
-
-Expand source code
-
-def unregister(self, client: str) -> None:
- """Unregister a client from the message bus.
-
- >>> async def callback(message):
- ... print(message)
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... [{'target': {'const': 'Client 1'}}],
- ... callback)
- ... bus.unregister('Client 1')
- >>> asyncio.run(main())
- """
- if client not in self._plugins:
- return
- event = Message('')
- event['event'] = 'unregistered'
- event['client'] = client
- del self._plugins[client]
- self._send_reg.delete(client)
- self._recv_reg.delete(client)
- if client in self._callbacks:
- del self._callbacks[client]
- self._queue.put_nowait(event)
-
-
-
-async def run (self) â>Â NoneType
-
-
-Run the message bus forever.
-
>>> async def main():
-... bus = MessageBus()
-... bus_task = asyncio.create_task(bus.run())
-... bus_task.cancel()
->>> asyncio.run(main())
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run the message bus forever.
-
- >>> async def main():
- ... bus = MessageBus()
- ... bus_task = asyncio.create_task(bus.run())
- ... bus_task.cancel()
- >>> asyncio.run(main())
- """
- while True:
- message = await self._queue.get()
- if ('target' in message and
- message['target'] == '' and
- 'command' in message and
- message['command'] == 'get clients'):
- for client in self._plugins:
- answer = Message('')
- answer['client'] = client
- answer['plugin'] = self._plugins[client]
- answer['sends'] = self._send_reg.get_templates(client)
- answer['receives'] = self._recv_reg.get_templates(client)
- await self._queue.put(answer)
- for client in self._recv_reg.get(message):
- await self._callbacks[client](message)
- self._queue.task_done()
-
-
-
-async def send (self, message:Â Message ) â>Â NoneType
-
-
-Send a message to the message bus.
-
>>> async def callback(message):
-... print(f"Got: {message}")
->>> async def main():
-... bus = MessageBus()
-... bus.register('Client 1', 'Test Plugin',
-... [{'k1': {'type': 'string'}}],
-... [{'target': {'const': 'Client 1'}}],
-... callback)
-... bus.register('Client 2', 'Test Plugin',
-... [{}],
-... [{'target': {'const': 'Client 2'}}],
-... callback)
-... bus_task = asyncio.create_task(bus.run())
-... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
-... 'k1': 'Test'})
-... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
-... try:
-... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
-... 'k1': 42})
-... except BusException as e:
-... print(e)
-... await asyncio.sleep(0)
-... bus_task.cancel()
->>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
-Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
-not allowed for sender 'Client 1'.
-Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
-Got: {'sender': 'Client 2', 'target': 'Client 1'}
-
-
-
-Expand source code
-
-async def send(self, message: Message) -> None:
- """Send a message to the message bus.
-
- >>> async def callback(message):
- ... print(f"Got: {message}")
- >>> async def main():
- ... bus = MessageBus()
- ... bus.register('Client 1', 'Test Plugin',
- ... [{'k1': {'type': 'string'}}],
- ... [{'target': {'const': 'Client 1'}}],
- ... callback)
- ... bus.register('Client 2', 'Test Plugin',
- ... [{}],
- ... [{'target': {'const': 'Client 2'}}],
- ... callback)
- ... bus_task = asyncio.create_task(bus.run())
- ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
- ... 'k1': 'Test'})
- ... await bus.send({'sender': 'Client 2', 'target': 'Client 1'})
- ... try:
- ... await bus.send({'sender': 'Client 1', 'target': 'Client 2',
- ... 'k1': 42})
- ... except BusException as e:
- ... print(e)
- ... await asyncio.sleep(0)
- ... bus_task.cancel()
- >>> asyncio.run(main()) # doctest: +NORMALIZE_WHITESPACE
- Message '{'sender': 'Client 1', 'target': 'Client 2', 'k1': 42}'
- not allowed for sender 'Client 1'.
- Got: {'sender': 'Client 1', 'target': 'Client 2', 'k1': 'Test'}
- Got: {'sender': 'Client 2', 'target': 'Client 1'}
- """
- assert isinstance(message['sender'], str)
- sender = message['sender']
- if sender:
- if not self._send_reg.check(sender, message):
- raise BusException(f"Message '{message}' not allowed for"
- f" sender '{sender}'.")
- await self._queue.put(message)
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/controlpi/pluginregistry.html b/doc/controlpi/pluginregistry.html
deleted file mode 100644
index ad9a464..0000000
--- a/doc/controlpi/pluginregistry.html
+++ /dev/null
@@ -1,429 +0,0 @@
-
-
-
-
-
-
-controlpi.pluginregistry API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Module controlpi.pluginregistry
-
-
-Provide a generic plugin system.
-The class PluginRegistry is initialised with the name of a namespace
-package and a base class.
-All modules in the namespace package are loaded. These modules can be
-included in different distribution packages, which allows to dynamically
-add plugins to the system without changing any code.
-Afterwards, all (direct and indirect) subclasses of the base class are
-registered as plugins under their class name. Class names should be unique,
-which cannot be programmatically enforced.
->>> class BasePlugin:
-... pass
->>> class Plugin1(BasePlugin):
-... pass
->>> class Plugin2(BasePlugin):
-... pass
->>> registry = PluginRegistry('importlib', BasePlugin)
-
-The registry provides a generic mapping interface with the class names as
-keys and the classes as values.
->>> print(len(registry))
-2
->>> for name in registry:
-... print(f"{name}: {registry[name]}")
-Plugin1: <class 'pluginregistry.Plugin1'>
-Plugin2: <class 'pluginregistry.Plugin2'>
->>> if 'Plugin1' in registry:
-... print(f"'Plugin1' is in registry.")
-'Plugin1' is in registry.
->>> p1 = registry['Plugin1']
->>> i1 = p1()
-
-
-
-Expand source code
-
-"""Provide a generic plugin system.
-
-The class PluginRegistry is initialised with the name of a namespace
-package and a base class.
-
-All modules in the namespace package are loaded. These modules can be
-included in different distribution packages, which allows to dynamically
-add plugins to the system without changing any code.
-
-Afterwards, all (direct and indirect) subclasses of the base class are
-registered as plugins under their class name. Class names should be unique,
-which cannot be programmatically enforced.
-
->>> class BasePlugin:
-... pass
->>> class Plugin1(BasePlugin):
-... pass
->>> class Plugin2(BasePlugin):
-... pass
->>> registry = PluginRegistry('importlib', BasePlugin)
-
-The registry provides a generic mapping interface with the class names as
-keys and the classes as values.
-
->>> print(len(registry))
-2
->>> for name in registry:
-... print(f"{name}: {registry[name]}")
-Plugin1: <class 'pluginregistry.Plugin1'>
-Plugin2: <class 'pluginregistry.Plugin2'>
->>> if 'Plugin1' in registry:
-... print(f"'Plugin1' is in registry.")
-'Plugin1' is in registry.
->>> p1 = registry['Plugin1']
->>> i1 = p1()
-"""
-import importlib
-import pkgutil
-import collections.abc
-
-
-class PluginRegistry(collections.abc.Mapping):
- """Provide a registry for plugins.
-
- Initialise the registry by loading all modules in the given namespace
- package and then registering all subclasses of the given base class as
- plugins (only simulated here â the code for Plugin1 and Plugin2 should
- be in modules in the given namespace package in real applications):
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
-
- After initialisation, provide a mapping interface to the plugins:
- >>> print(len(registry))
- 2
- >>> for name in registry:
- ... print(f"{name}: {registry[name]}")
- Plugin1: <class 'pluginregistry.Plugin1'>
- Plugin2: <class 'pluginregistry.Plugin2'>
- >>> if 'Plugin1' in registry:
- ... print(f"'Plugin1' is in registry.")
- 'Plugin1' is in registry.
- """
-
- def __init__(self, namespace_package: str, base_class: type) -> None:
- """Initialise registry.
-
- Import all modules defined in the given namespace package (in any
- distribution package currently installed in the path). Then register
- all subclasses of the given base class as plugins.
-
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
- >>> for name in registry._plugins:
- ... print(f"{name}: {registry._plugins[name]}")
- Plugin1: <class 'pluginregistry.Plugin1'>
- Plugin2: <class 'pluginregistry.Plugin2'>
- """
- ns_mod = importlib.import_module(namespace_package)
- ns_path = ns_mod.__path__ # type: ignore # mypy issue #1422
- ns_name = ns_mod.__name__
- for _, mod_name, _ in pkgutil.iter_modules(ns_path):
- importlib.import_module(f"{ns_name}.{mod_name}")
-
- def all_subclasses(cls):
- result = []
- for subcls in cls.__subclasses__():
- result.append(subcls)
- result.extend(all_subclasses(subcls))
- return result
- self._plugins = {cls.__name__: cls
- for cls in all_subclasses(base_class)}
-
- def __len__(self) -> int:
- """Get number of registered plugins.
-
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
- >>> print(registry.__len__())
- 2
- """
- return len(self._plugins)
-
- def __iter__(self):
- """Get an iterator of the registered plugins.
-
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
- >>> print(type(registry.__iter__()))
- <class 'dict_keyiterator'>
- >>> for name in registry:
- ... print(name)
- Plugin1
- Plugin2
- """
- return iter(self._plugins)
-
- def __getitem__(self, plugin_name: str) -> type:
- """Get a registered plugin given its name.
-
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
- >>> print(registry.__getitem__('Plugin1'))
- <class 'pluginregistry.Plugin1'>
- >>> print(registry.__getitem__('Plugin2'))
- <class 'pluginregistry.Plugin2'>
- >>> for name in registry:
- ... print(registry[name])
- <class 'pluginregistry.Plugin1'>
- <class 'pluginregistry.Plugin2'>
- """
- return self._plugins[plugin_name]
-
-
-
-
-
-
-
-
-
-class PluginRegistry
-( namespace_package:Â str, base_class:Â type)
-
-
-Provide a registry for plugins.
-
Initialise the registry by loading all modules in the given namespace
-package and then registering all subclasses of the given base class as
-plugins (only simulated here â the code for Plugin1 and Plugin2 should
-be in modules in the given namespace package in real applications):
-
>>> class BasePlugin:
-... pass
->>> class Plugin1(BasePlugin):
-... pass
->>> class Plugin2(BasePlugin):
-... pass
->>> registry = PluginRegistry('importlib', BasePlugin)
-
-
After initialisation, provide a mapping interface to the plugins:
-
>>> print(len(registry))
-2
->>> for name in registry:
-... print(f"{name}: {registry[name]}")
-Plugin1: <class 'pluginregistry.Plugin1'>
-Plugin2: <class 'pluginregistry.Plugin2'>
->>> if 'Plugin1' in registry:
-... print(f"'Plugin1' is in registry.")
-'Plugin1' is in registry.
-
-
Initialise registry.
-
Import all modules defined in the given namespace package (in any
-distribution package currently installed in the path). Then register
-all subclasses of the given base class as plugins.
-
>>> class BasePlugin:
-... pass
->>> class Plugin1(BasePlugin):
-... pass
->>> class Plugin2(BasePlugin):
-... pass
->>> registry = PluginRegistry('importlib', BasePlugin)
->>> for name in registry._plugins:
-... print(f"{name}: {registry._plugins[name]}")
-Plugin1: <class 'pluginregistry.Plugin1'>
-Plugin2: <class 'pluginregistry.Plugin2'>
-
-
-
-Expand source code
-
-class PluginRegistry(collections.abc.Mapping):
- """Provide a registry for plugins.
-
- Initialise the registry by loading all modules in the given namespace
- package and then registering all subclasses of the given base class as
- plugins (only simulated here â the code for Plugin1 and Plugin2 should
- be in modules in the given namespace package in real applications):
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
-
- After initialisation, provide a mapping interface to the plugins:
- >>> print(len(registry))
- 2
- >>> for name in registry:
- ... print(f"{name}: {registry[name]}")
- Plugin1: <class 'pluginregistry.Plugin1'>
- Plugin2: <class 'pluginregistry.Plugin2'>
- >>> if 'Plugin1' in registry:
- ... print(f"'Plugin1' is in registry.")
- 'Plugin1' is in registry.
- """
-
- def __init__(self, namespace_package: str, base_class: type) -> None:
- """Initialise registry.
-
- Import all modules defined in the given namespace package (in any
- distribution package currently installed in the path). Then register
- all subclasses of the given base class as plugins.
-
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
- >>> for name in registry._plugins:
- ... print(f"{name}: {registry._plugins[name]}")
- Plugin1: <class 'pluginregistry.Plugin1'>
- Plugin2: <class 'pluginregistry.Plugin2'>
- """
- ns_mod = importlib.import_module(namespace_package)
- ns_path = ns_mod.__path__ # type: ignore # mypy issue #1422
- ns_name = ns_mod.__name__
- for _, mod_name, _ in pkgutil.iter_modules(ns_path):
- importlib.import_module(f"{ns_name}.{mod_name}")
-
- def all_subclasses(cls):
- result = []
- for subcls in cls.__subclasses__():
- result.append(subcls)
- result.extend(all_subclasses(subcls))
- return result
- self._plugins = {cls.__name__: cls
- for cls in all_subclasses(base_class)}
-
- def __len__(self) -> int:
- """Get number of registered plugins.
-
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
- >>> print(registry.__len__())
- 2
- """
- return len(self._plugins)
-
- def __iter__(self):
- """Get an iterator of the registered plugins.
-
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
- >>> print(type(registry.__iter__()))
- <class 'dict_keyiterator'>
- >>> for name in registry:
- ... print(name)
- Plugin1
- Plugin2
- """
- return iter(self._plugins)
-
- def __getitem__(self, plugin_name: str) -> type:
- """Get a registered plugin given its name.
-
- >>> class BasePlugin:
- ... pass
- >>> class Plugin1(BasePlugin):
- ... pass
- >>> class Plugin2(BasePlugin):
- ... pass
- >>> registry = PluginRegistry('importlib', BasePlugin)
- >>> print(registry.__getitem__('Plugin1'))
- <class 'pluginregistry.Plugin1'>
- >>> print(registry.__getitem__('Plugin2'))
- <class 'pluginregistry.Plugin2'>
- >>> for name in registry:
- ... print(registry[name])
- <class 'pluginregistry.Plugin1'>
- <class 'pluginregistry.Plugin2'>
- """
- return self._plugins[plugin_name]
-
-Ancestors
-
-collections.abc.Mapping
-collections.abc.Collection
-collections.abc.Sized
-collections.abc.Iterable
-collections.abc.Container
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/controlpi_plugins/index.html b/doc/controlpi_plugins/index.html
deleted file mode 100644
index 6775c5d..0000000
--- a/doc/controlpi_plugins/index.html
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-controlpi_plugins API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Namespace controlpi_plugins
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/controlpi_plugins/state.html b/doc/controlpi_plugins/state.html
deleted file mode 100644
index 8f33480..0000000
--- a/doc/controlpi_plugins/state.html
+++ /dev/null
@@ -1,1884 +0,0 @@
-
-
-
-
-
-
-controlpi_plugins.state API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Module controlpi_plugins.state
-
-
-Provide state plugins for all kinds of systems.
-
-State represents a Boolean state.
-StateAlias translates to another state-like client.
-AndState combines several state-like clients by conjunction.
-OrState combines several state-like clients by disjunction.
-
-All these plugins use the following conventions:
-
-If their state changes they send a message containing "event": "changed"
-and "state": .
-If their state is reported due to a message, but did not change they send
-a message containing just "state": .
-If they receive a message containing "target": and
-"command": "get state" they report their current state.
-If State (or any other settable state using these conventions) receives
-a message containing "target": , "command": "set state" and
-"new state": it changes the state accordingly. If this
-was really a change the corresponding event is sent. If it was already in
-this state a report message without "event": "changed" is sent.
-StateAlias can alias any message bus client using these conventions, not
-just State instances. It translates all messages described here in both
-directions.
-AndState and OrState instances cannot be set.
-AndState and OrState can combine any message bus clients using these
-conventions, not just State instances. They only react to messages
-containing "state" information.
-
->>> import asyncio
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test State": {"plugin": "State"},
-... "Test State 2": {"plugin": "State"},
-... "Test StateAlias": {"plugin": "StateAlias",
-... "alias for": "Test State 2"},
-... "Test AndState": {"plugin": "AndState",
-... "states": ["Test State", "Test StateAlias"]},
-... "Test OrState": {"plugin": "OrState",
-... "states": ["Test State", "Test StateAlias"]}},
-... [{"target": "Test AndState",
-... "command": "get state"},
-... {"target": "Test OrState",
-... "command": "get state"},
-... {"target": "Test State",
-... "command": "set state", "new state": True},
-... {"target": "Test StateAlias",
-... "command": "set state", "new state": True},
-... {"target": "Test State",
-... "command": "set state", "new state": False}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 2', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test StateAlias', 'plugin': 'StateAlias',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test AndState', 'plugin': 'AndState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test AndState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test StateAlias'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test OrState', 'plugin': 'OrState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test OrState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test StateAlias'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': 'test()', 'target': 'Test AndState',
- 'command': 'get state'}
-test(): {'sender': 'Test AndState', 'state': False}
-test(): {'sender': 'test()', 'target': 'Test OrState',
- 'command': 'get state'}
-test(): {'sender': 'Test OrState', 'state': False}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test StateAlias',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test StateAlias', 'target': 'Test State 2',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test AndState', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': False}
-test(): {'sender': 'Test State', 'event': 'changed', 'state': False}
-test(): {'sender': 'Test AndState', 'event': 'changed', 'state': False}
-
-
-
-Expand source code
-
-"""Provide state plugins for all kinds of systems.
-
-- State represents a Boolean state.
-- StateAlias translates to another state-like client.
-- AndState combines several state-like clients by conjunction.
-- OrState combines several state-like clients by disjunction.
-
-All these plugins use the following conventions:
-
-- If their state changes they send a message containing "event": "changed"
- and "state": <new state>.
-- If their state is reported due to a message, but did not change they send
- a message containing just "state": <current state>.
-- If they receive a message containing "target": <name> and
- "command": "get state" they report their current state.
-- If State (or any other settable state using these conventions) receives
- a message containing "target": <name>, "command": "set state" and
- "new state": <state to be set> it changes the state accordingly. If this
- was really a change the corresponding event is sent. If it was already in
- this state a report message without "event": "changed" is sent.
-- StateAlias can alias any message bus client using these conventions, not
- just State instances. It translates all messages described here in both
- directions.
-- AndState and OrState instances cannot be set.
-- AndState and OrState can combine any message bus clients using these
- conventions, not just State instances. They only react to messages
- containing "state" information.
-
->>> import asyncio
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test State": {"plugin": "State"},
-... "Test State 2": {"plugin": "State"},
-... "Test StateAlias": {"plugin": "StateAlias",
-... "alias for": "Test State 2"},
-... "Test AndState": {"plugin": "AndState",
-... "states": ["Test State", "Test StateAlias"]},
-... "Test OrState": {"plugin": "OrState",
-... "states": ["Test State", "Test StateAlias"]}},
-... [{"target": "Test AndState",
-... "command": "get state"},
-... {"target": "Test OrState",
-... "command": "get state"},
-... {"target": "Test State",
-... "command": "set state", "new state": True},
-... {"target": "Test StateAlias",
-... "command": "set state", "new state": True},
-... {"target": "Test State",
-... "command": "set state", "new state": False}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 2', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test StateAlias', 'plugin': 'StateAlias',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test AndState', 'plugin': 'AndState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test AndState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test StateAlias'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test OrState', 'plugin': 'OrState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test OrState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test StateAlias'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': 'test()', 'target': 'Test AndState',
- 'command': 'get state'}
-test(): {'sender': 'Test AndState', 'state': False}
-test(): {'sender': 'test()', 'target': 'Test OrState',
- 'command': 'get state'}
-test(): {'sender': 'Test OrState', 'state': False}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test StateAlias',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test StateAlias', 'target': 'Test State 2',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test AndState', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': False}
-test(): {'sender': 'Test State', 'event': 'changed', 'state': False}
-test(): {'sender': 'Test AndState', 'event': 'changed', 'state': False}
-"""
-from controlpi import BasePlugin, Message, MessageTemplate
-
-from typing import Dict
-
-
-class State(BasePlugin):
- """Provide a Boolean state.
-
- The state of a State plugin instance can be queried with the "get state"
- command and set with the "set state" command to the new state given by
- the "new state" key:
- >>> import asyncio
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test State": {"plugin": "State"}},
- ... [{"target": "Test State", "command": "get state"},
- ... {"target": "Test State", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State", "command": "get state"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
- test(): {'sender': 'Test State', 'state': False}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
- test(): {'sender': 'Test State', 'state': True}
- """
-
- CONF_SCHEMA = True
- """Schema for State plugin configuration.
-
- There are no required or optional configuration keys.
- """
-
- async def receive(self, message: Message) -> None:
- """Process commands to get/set state."""
- if message['command'] == 'get state':
- await self.bus.send(Message(self.name, {'state': self.state}))
- elif message['command'] == 'set state':
- if self.state != message['new state']:
- assert isinstance(message['new state'], bool)
- self.state: bool = message['new state']
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
- else:
- await self.bus.send(Message(self.name,
- {'state': self.state}))
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.state = False
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}})]
- self.bus.register(self.name, 'State', sends, receives, self.receive)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-class StateAlias(BasePlugin):
- """Define an alias for another state.
-
- The "alias for" configuration key gets the name for the other state that
- is aliased by the StateAlias plugin instance.
-
- The "get state" and "set state" commands are forwarded to and the
- "changed" events and "state" messages are forwarded from this other
- state:
- >>> import asyncio
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test State": {"plugin": "State"},
- ... "Test StateAlias": {"plugin": "StateAlias",
- ... "alias for": "Test State"}},
- ... [{"target": "Test State", "command": "get state"},
- ... {"target": "Test StateAlias", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State", "command": "set state",
- ... "new state": True},
- ... {"target": "Test StateAlias", "command": "get state"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test StateAlias', 'plugin': 'StateAlias',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State'},
- 'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State'},
- 'state': {'type': 'boolean'}}]}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
- test(): {'sender': 'Test State', 'state': False}
- test(): {'sender': 'Test StateAlias', 'state': False}
- test(): {'sender': 'test()', 'target': 'Test StateAlias',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test StateAlias', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
- test(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State', 'state': True}
- test(): {'sender': 'Test StateAlias', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test StateAlias',
- 'command': 'get state'}
- test(): {'sender': 'Test StateAlias', 'target': 'Test State',
- 'command': 'get state'}
- test(): {'sender': 'Test State', 'state': True}
- test(): {'sender': 'Test StateAlias', 'state': True}
- """
-
- CONF_SCHEMA = {'properties': {'alias for': {'type': 'string'}},
- 'required': ['alias for']}
- """Schema for StateAlias plugin configuration.
-
- Required configuration key:
-
- - 'alias for': name of aliased state.
- """
-
- async def receive(self, message: Message) -> None:
- """Translate states from and commands to aliased state."""
- alias_message = Message(self.name)
- if ('target' in message and message['target'] == self.name and
- 'command' in message):
- alias_message['target'] = self.conf['alias for']
- if message['command'] == 'get state':
- alias_message['command'] = 'get state'
- await self.bus.send(alias_message)
- elif (message['command'] == 'set state' and
- 'new state' in message):
- alias_message['command'] = 'set state'
- alias_message['new state'] = message['new state']
- await self.bus.send(alias_message)
- if (message['sender'] == self.conf['alias for'] and
- 'state' in message):
- if 'event' in message and message['event'] == 'changed':
- alias_message['event'] = 'changed'
- alias_message['state'] = message['state']
- await self.bus.send(alias_message)
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'target': {'const': self.conf['alias for']},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.conf['alias for']},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}),
- MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}),
- MessageTemplate({'sender':
- {'const': self.conf['alias for']},
- 'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'sender':
- {'const': self.conf['alias for']},
- 'state': {'type': 'boolean'}})]
- self.bus.register(self.name, 'StateAlias',
- sends, receives, self.receive)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-class AndState(BasePlugin):
- """Implement conjunction of states.
-
- The "states" configuration key gets an array of states to be combined.
- An AndState plugin client reacts to "get state" commands and sends
- "changed" events when a change in one of the combined states leads to
- a change for the conjunction:
- >>> import asyncio
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test State 1": {"plugin": "State"},
- ... "Test State 2": {"plugin": "State"},
- ... "Test AndState": {"plugin": "AndState",
- ... "states": ["Test State 1", "Test State 2"]}},
- ... [{"target": "Test State 1", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State 2", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State 1", "command": "set state",
- ... "new state": False},
- ... {"target": "Test AndState", "command": "get state"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 1', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 1'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 1'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 2', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test AndState', 'plugin': 'AndState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test AndState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State 1'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'state': {'type': 'boolean'}}]}
- test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State 2',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
- test(): {'sender': 'Test AndState', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': False}
- test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
- test(): {'sender': 'Test AndState', 'event': 'changed', 'state': False}
- test(): {'sender': 'test()', 'target': 'Test AndState',
- 'command': 'get state'}
- test(): {'sender': 'Test AndState', 'state': False}
- """
-
- CONF_SCHEMA = {'properties': {'states': {'type': 'array',
- 'items': {'type': 'string'}}},
- 'required': ['states']}
- """Schema for AndState plugin configuration.
-
- Required configuration key:
-
- - 'states': list of names of combined states.
- """
-
- async def receive(self, message: Message) -> None:
- """Process "get state" command and messages of combined states."""
- if ('target' in message and message['target'] == self.name and
- 'command' in message and message['command'] == 'get state'):
- await self.bus.send(Message(self.name, {'state': self.state}))
- if 'state' in message and message['sender'] in self.conf['states']:
- assert isinstance(message['sender'], str)
- assert isinstance(message['state'], bool)
- self.states[message['sender']] = message['state']
- new_state = all(self.states.values())
- if self.state != new_state:
- self.state: bool = new_state
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}})]
- self.states: Dict[str, bool] = {}
- for state in self.conf['states']:
- receives.append(MessageTemplate({'sender': {'const': state},
- 'state': {'type': 'boolean'}}))
- self.states[state] = False
- self.state = all(self.states.values())
- self.bus.register(self.name, 'AndState',
- sends, receives, self.receive)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-class OrState(BasePlugin):
- """Implement disjunction of states.
-
- The "states" configuration key gets an array of states to be combined.
- An OrState plugin client reacts to "get state" commands and sends
- "changed" events when a change in one of the combined states leads to
- a change for the disjunction:
- >>> import asyncio
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test State 1": {"plugin": "State"},
- ... "Test State 2": {"plugin": "State"},
- ... "Test OrState": {"plugin": "OrState",
- ... "states": ["Test State 1", "Test State 2"]}},
- ... [{"target": "Test State 1", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State 2", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State 1", "command": "set state",
- ... "new state": False},
- ... {"target": "Test OrState", "command": "get state"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 1', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 1'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 1'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 2', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test OrState', 'plugin': 'OrState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test OrState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State 1'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'state': {'type': 'boolean'}}]}
- test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
- test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State 2',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': False}
- test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
- test(): {'sender': 'test()', 'target': 'Test OrState',
- 'command': 'get state'}
- test(): {'sender': 'Test OrState', 'state': True}
- """
-
- CONF_SCHEMA = {'properties': {'states': {'type': 'array',
- 'items': {'type': 'string'}}},
- 'required': ['states']}
- """Schema for OrState plugin configuration.
-
- Required configuration key:
-
- - 'states': list of names of combined states.
- """
-
- async def receive(self, message: Message) -> None:
- """Process "get state" command and messages of combined states."""
- if ('target' in message and message['target'] == self.name and
- 'command' in message and message['command'] == 'get state'):
- await self.bus.send(Message(self.name, {'state': self.state}))
- if 'state' in message and message['sender'] in self.conf['states']:
- assert isinstance(message['sender'], str)
- assert isinstance(message['state'], bool)
- self.states[message['sender']] = message['state']
- new_state = any(self.states.values())
- if self.state != new_state:
- self.state: bool = new_state
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}})]
- self.states: Dict[str, bool] = {}
- for state in self.conf['states']:
- receives.append(MessageTemplate({'sender': {'const': state},
- 'state': {'type': 'boolean'}}))
- self.states[state] = False
- self.state = any(self.states.values())
- self.bus.register(self.name, 'OrState',
- sends, receives, self.receive)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-
-
-
-
-class State
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Provide a Boolean state.
-
The state of a State plugin instance can be queried with the "get state"
-command and set with the "set state" command to the new state given by
-the "new state" key:
-
>>> import asyncio
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test State": {"plugin": "State"}},
-... [{"target": "Test State", "command": "get state"},
-... {"target": "Test State", "command": "set state",
-... "new state": True},
-... {"target": "Test State", "command": "set state",
-... "new state": True},
-... {"target": "Test State", "command": "get state"}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
-test(): {'sender': 'Test State', 'state': False}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
-test(): {'sender': 'Test State', 'state': True}
-
-
-
-Expand source code
-
-class State(BasePlugin):
- """Provide a Boolean state.
-
- The state of a State plugin instance can be queried with the "get state"
- command and set with the "set state" command to the new state given by
- the "new state" key:
- >>> import asyncio
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test State": {"plugin": "State"}},
- ... [{"target": "Test State", "command": "get state"},
- ... {"target": "Test State", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State", "command": "get state"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
- test(): {'sender': 'Test State', 'state': False}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
- test(): {'sender': 'Test State', 'state': True}
- """
-
- CONF_SCHEMA = True
- """Schema for State plugin configuration.
-
- There are no required or optional configuration keys.
- """
-
- async def receive(self, message: Message) -> None:
- """Process commands to get/set state."""
- if message['command'] == 'get state':
- await self.bus.send(Message(self.name, {'state': self.state}))
- elif message['command'] == 'set state':
- if self.state != message['new state']:
- assert isinstance(message['new state'], bool)
- self.state: bool = message['new state']
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
- else:
- await self.bus.send(Message(self.name,
- {'state': self.state}))
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.state = False
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}})]
- self.bus.register(self.name, 'State', sends, receives, self.receive)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for State plugin configuration.
-
There are no required or optional configuration keys.
-
-
-Methods
-
-
-async def receive (self, message:Â Message ) â>Â NoneType
-
-
-Process commands to get/set state.
-
-
-Expand source code
-
-async def receive(self, message: Message) -> None:
- """Process commands to get/set state."""
- if message['command'] == 'get state':
- await self.bus.send(Message(self.name, {'state': self.state}))
- elif message['command'] == 'set state':
- if self.state != message['new state']:
- assert isinstance(message['new state'], bool)
- self.state: bool = message['new state']
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
- else:
- await self.bus.send(Message(self.name,
- {'state': self.state}))
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.state = False
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}})]
- self.bus.register(self.name, 'State', sends, receives, self.receive)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-class StateAlias
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Define an alias for another state.
-
The "alias for" configuration key gets the name for the other state that
-is aliased by the StateAlias plugin instance.
-
The "get state" and "set state" commands are forwarded to and the
-"changed" events and "state" messages are forwarded from this other
-state:
-
>>> import asyncio
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test State": {"plugin": "State"},
-... "Test StateAlias": {"plugin": "StateAlias",
-... "alias for": "Test State"}},
-... [{"target": "Test State", "command": "get state"},
-... {"target": "Test StateAlias", "command": "set state",
-... "new state": True},
-... {"target": "Test State", "command": "set state",
-... "new state": True},
-... {"target": "Test StateAlias", "command": "get state"}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test StateAlias', 'plugin': 'StateAlias',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State'},
- 'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
-test(): {'sender': 'Test State', 'state': False}
-test(): {'sender': 'Test StateAlias', 'state': False}
-test(): {'sender': 'test()', 'target': 'Test StateAlias',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test StateAlias', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State', 'state': True}
-test(): {'sender': 'Test StateAlias', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test StateAlias',
- 'command': 'get state'}
-test(): {'sender': 'Test StateAlias', 'target': 'Test State',
- 'command': 'get state'}
-test(): {'sender': 'Test State', 'state': True}
-test(): {'sender': 'Test StateAlias', 'state': True}
-
-
-
-Expand source code
-
-class StateAlias(BasePlugin):
- """Define an alias for another state.
-
- The "alias for" configuration key gets the name for the other state that
- is aliased by the StateAlias plugin instance.
-
- The "get state" and "set state" commands are forwarded to and the
- "changed" events and "state" messages are forwarded from this other
- state:
- >>> import asyncio
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test State": {"plugin": "State"},
- ... "Test StateAlias": {"plugin": "StateAlias",
- ... "alias for": "Test State"}},
- ... [{"target": "Test State", "command": "get state"},
- ... {"target": "Test StateAlias", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State", "command": "set state",
- ... "new state": True},
- ... {"target": "Test StateAlias", "command": "get state"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test StateAlias', 'plugin': 'StateAlias',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test StateAlias'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State'},
- 'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State'},
- 'state': {'type': 'boolean'}}]}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'get state'}
- test(): {'sender': 'Test State', 'state': False}
- test(): {'sender': 'Test StateAlias', 'state': False}
- test(): {'sender': 'test()', 'target': 'Test StateAlias',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test StateAlias', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State', 'event': 'changed', 'state': True}
- test(): {'sender': 'Test StateAlias', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State', 'state': True}
- test(): {'sender': 'Test StateAlias', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test StateAlias',
- 'command': 'get state'}
- test(): {'sender': 'Test StateAlias', 'target': 'Test State',
- 'command': 'get state'}
- test(): {'sender': 'Test State', 'state': True}
- test(): {'sender': 'Test StateAlias', 'state': True}
- """
-
- CONF_SCHEMA = {'properties': {'alias for': {'type': 'string'}},
- 'required': ['alias for']}
- """Schema for StateAlias plugin configuration.
-
- Required configuration key:
-
- - 'alias for': name of aliased state.
- """
-
- async def receive(self, message: Message) -> None:
- """Translate states from and commands to aliased state."""
- alias_message = Message(self.name)
- if ('target' in message and message['target'] == self.name and
- 'command' in message):
- alias_message['target'] = self.conf['alias for']
- if message['command'] == 'get state':
- alias_message['command'] = 'get state'
- await self.bus.send(alias_message)
- elif (message['command'] == 'set state' and
- 'new state' in message):
- alias_message['command'] = 'set state'
- alias_message['new state'] = message['new state']
- await self.bus.send(alias_message)
- if (message['sender'] == self.conf['alias for'] and
- 'state' in message):
- if 'event' in message and message['event'] == 'changed':
- alias_message['event'] = 'changed'
- alias_message['state'] = message['state']
- await self.bus.send(alias_message)
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'target': {'const': self.conf['alias for']},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.conf['alias for']},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}),
- MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}),
- MessageTemplate({'sender':
- {'const': self.conf['alias for']},
- 'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'sender':
- {'const': self.conf['alias for']},
- 'state': {'type': 'boolean'}})]
- self.bus.register(self.name, 'StateAlias',
- sends, receives, self.receive)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for StateAlias plugin configuration.
-
Required configuration key:
-
-'alias for': name of aliased state.
-
-
-
-Methods
-
-
-async def receive (self, message:Â Message ) â>Â NoneType
-
-
-Translate states from and commands to aliased state.
-
-
-Expand source code
-
-async def receive(self, message: Message) -> None:
- """Translate states from and commands to aliased state."""
- alias_message = Message(self.name)
- if ('target' in message and message['target'] == self.name and
- 'command' in message):
- alias_message['target'] = self.conf['alias for']
- if message['command'] == 'get state':
- alias_message['command'] = 'get state'
- await self.bus.send(alias_message)
- elif (message['command'] == 'set state' and
- 'new state' in message):
- alias_message['command'] = 'set state'
- alias_message['new state'] = message['new state']
- await self.bus.send(alias_message)
- if (message['sender'] == self.conf['alias for'] and
- 'state' in message):
- if 'event' in message and message['event'] == 'changed':
- alias_message['event'] = 'changed'
- alias_message['state'] = message['state']
- await self.bus.send(alias_message)
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'target': {'const': self.conf['alias for']},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.conf['alias for']},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}),
- MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}),
- MessageTemplate({'sender':
- {'const': self.conf['alias for']},
- 'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'sender':
- {'const': self.conf['alias for']},
- 'state': {'type': 'boolean'}})]
- self.bus.register(self.name, 'StateAlias',
- sends, receives, self.receive)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-class AndState
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Implement conjunction of states.
-
The "states" configuration key gets an array of states to be combined.
-An AndState plugin client reacts to "get state" commands and sends
-"changed" events when a change in one of the combined states leads to
-a change for the conjunction:
-
>>> import asyncio
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test State 1": {"plugin": "State"},
-... "Test State 2": {"plugin": "State"},
-... "Test AndState": {"plugin": "AndState",
-... "states": ["Test State 1", "Test State 2"]}},
-... [{"target": "Test State 1", "command": "set state",
-... "new state": True},
-... {"target": "Test State 2", "command": "set state",
-... "new state": True},
-... {"target": "Test State 1", "command": "set state",
-... "new state": False},
-... {"target": "Test AndState", "command": "get state"}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 1', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 1'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 1'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 2', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test AndState', 'plugin': 'AndState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test AndState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State 1'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State 2',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test AndState', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': False}
-test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
-test(): {'sender': 'Test AndState', 'event': 'changed', 'state': False}
-test(): {'sender': 'test()', 'target': 'Test AndState',
- 'command': 'get state'}
-test(): {'sender': 'Test AndState', 'state': False}
-
-
-
-Expand source code
-
-class AndState(BasePlugin):
- """Implement conjunction of states.
-
- The "states" configuration key gets an array of states to be combined.
- An AndState plugin client reacts to "get state" commands and sends
- "changed" events when a change in one of the combined states leads to
- a change for the conjunction:
- >>> import asyncio
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test State 1": {"plugin": "State"},
- ... "Test State 2": {"plugin": "State"},
- ... "Test AndState": {"plugin": "AndState",
- ... "states": ["Test State 1", "Test State 2"]}},
- ... [{"target": "Test State 1", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State 2", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State 1", "command": "set state",
- ... "new state": False},
- ... {"target": "Test AndState", "command": "get state"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 1', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 1'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 1'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 2', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test AndState', 'plugin': 'AndState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test AndState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State 1'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'state': {'type': 'boolean'}}]}
- test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State 2',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
- test(): {'sender': 'Test AndState', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': False}
- test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
- test(): {'sender': 'Test AndState', 'event': 'changed', 'state': False}
- test(): {'sender': 'test()', 'target': 'Test AndState',
- 'command': 'get state'}
- test(): {'sender': 'Test AndState', 'state': False}
- """
-
- CONF_SCHEMA = {'properties': {'states': {'type': 'array',
- 'items': {'type': 'string'}}},
- 'required': ['states']}
- """Schema for AndState plugin configuration.
-
- Required configuration key:
-
- - 'states': list of names of combined states.
- """
-
- async def receive(self, message: Message) -> None:
- """Process "get state" command and messages of combined states."""
- if ('target' in message and message['target'] == self.name and
- 'command' in message and message['command'] == 'get state'):
- await self.bus.send(Message(self.name, {'state': self.state}))
- if 'state' in message and message['sender'] in self.conf['states']:
- assert isinstance(message['sender'], str)
- assert isinstance(message['state'], bool)
- self.states[message['sender']] = message['state']
- new_state = all(self.states.values())
- if self.state != new_state:
- self.state: bool = new_state
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}})]
- self.states: Dict[str, bool] = {}
- for state in self.conf['states']:
- receives.append(MessageTemplate({'sender': {'const': state},
- 'state': {'type': 'boolean'}}))
- self.states[state] = False
- self.state = all(self.states.values())
- self.bus.register(self.name, 'AndState',
- sends, receives, self.receive)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for AndState plugin configuration.
-
Required configuration key:
-
-'states': list of names of combined states.
-
-
-
-Methods
-
-
-async def receive (self, message:Â Message ) â>Â NoneType
-
-
-Process "get state" command and messages of combined states.
-
-
-Expand source code
-
-async def receive(self, message: Message) -> None:
- """Process "get state" command and messages of combined states."""
- if ('target' in message and message['target'] == self.name and
- 'command' in message and message['command'] == 'get state'):
- await self.bus.send(Message(self.name, {'state': self.state}))
- if 'state' in message and message['sender'] in self.conf['states']:
- assert isinstance(message['sender'], str)
- assert isinstance(message['state'], bool)
- self.states[message['sender']] = message['state']
- new_state = all(self.states.values())
- if self.state != new_state:
- self.state: bool = new_state
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}})]
- self.states: Dict[str, bool] = {}
- for state in self.conf['states']:
- receives.append(MessageTemplate({'sender': {'const': state},
- 'state': {'type': 'boolean'}}))
- self.states[state] = False
- self.state = all(self.states.values())
- self.bus.register(self.name, 'AndState',
- sends, receives, self.receive)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-class OrState
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Implement disjunction of states.
-
The "states" configuration key gets an array of states to be combined.
-An OrState plugin client reacts to "get state" commands and sends
-"changed" events when a change in one of the combined states leads to
-a change for the disjunction:
-
>>> import asyncio
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test State 1": {"plugin": "State"},
-... "Test State 2": {"plugin": "State"},
-... "Test OrState": {"plugin": "OrState",
-... "states": ["Test State 1", "Test State 2"]}},
-... [{"target": "Test State 1", "command": "set state",
-... "new state": True},
-... {"target": "Test State 2", "command": "set state",
-... "new state": True},
-... {"target": "Test State 1", "command": "set state",
-... "new state": False},
-... {"target": "Test OrState", "command": "get state"}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 1', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 1'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 1'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 2', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test OrState', 'plugin': 'OrState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test OrState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State 1'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'state': {'type': 'boolean'}}]}
-test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
-test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State 2',
- 'command': 'set state', 'new state': True}
-test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
-test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': False}
-test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
-test(): {'sender': 'test()', 'target': 'Test OrState',
- 'command': 'get state'}
-test(): {'sender': 'Test OrState', 'state': True}
-
-
-
-Expand source code
-
-class OrState(BasePlugin):
- """Implement disjunction of states.
-
- The "states" configuration key gets an array of states to be combined.
- An OrState plugin client reacts to "get state" commands and sends
- "changed" events when a change in one of the combined states leads to
- a change for the disjunction:
- >>> import asyncio
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test State 1": {"plugin": "State"},
- ... "Test State 2": {"plugin": "State"},
- ... "Test OrState": {"plugin": "OrState",
- ... "states": ["Test State 1", "Test State 2"]}},
- ... [{"target": "Test State 1", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State 2", "command": "set state",
- ... "new state": True},
- ... {"target": "Test State 1", "command": "set state",
- ... "new state": False},
- ... {"target": "Test OrState", "command": "get state"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 1', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 1'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 1'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test State 2', 'plugin': 'State',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test State 2'},
- 'command': {'const': 'get state'}},
- {'target': {'const': 'Test State 2'},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test OrState', 'plugin': 'OrState',
- 'sends': [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}],
- 'receives': [{'target': {'const': 'Test OrState'},
- 'command': {'const': 'get state'}},
- {'sender': {'const': 'Test State 1'},
- 'state': {'type': 'boolean'}},
- {'sender': {'const': 'Test State 2'},
- 'state': {'type': 'boolean'}}]}
- test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State 1', 'event': 'changed', 'state': True}
- test(): {'sender': 'Test OrState', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State 2',
- 'command': 'set state', 'new state': True}
- test(): {'sender': 'Test State 2', 'event': 'changed', 'state': True}
- test(): {'sender': 'test()', 'target': 'Test State 1',
- 'command': 'set state', 'new state': False}
- test(): {'sender': 'Test State 1', 'event': 'changed', 'state': False}
- test(): {'sender': 'test()', 'target': 'Test OrState',
- 'command': 'get state'}
- test(): {'sender': 'Test OrState', 'state': True}
- """
-
- CONF_SCHEMA = {'properties': {'states': {'type': 'array',
- 'items': {'type': 'string'}}},
- 'required': ['states']}
- """Schema for OrState plugin configuration.
-
- Required configuration key:
-
- - 'states': list of names of combined states.
- """
-
- async def receive(self, message: Message) -> None:
- """Process "get state" command and messages of combined states."""
- if ('target' in message and message['target'] == self.name and
- 'command' in message and message['command'] == 'get state'):
- await self.bus.send(Message(self.name, {'state': self.state}))
- if 'state' in message and message['sender'] in self.conf['states']:
- assert isinstance(message['sender'], str)
- assert isinstance(message['state'], bool)
- self.states[message['sender']] = message['state']
- new_state = any(self.states.values())
- if self.state != new_state:
- self.state: bool = new_state
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}})]
- self.states: Dict[str, bool] = {}
- for state in self.conf['states']:
- receives.append(MessageTemplate({'sender': {'const': state},
- 'state': {'type': 'boolean'}}))
- self.states[state] = False
- self.state = any(self.states.values())
- self.bus.register(self.name, 'OrState',
- sends, receives, self.receive)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for OrState plugin configuration.
-
Required configuration key:
-
-'states': list of names of combined states.
-
-
-
-Methods
-
-
-async def receive (self, message:Â Message ) â>Â NoneType
-
-
-Process "get state" command and messages of combined states.
-
-
-Expand source code
-
-async def receive(self, message: Message) -> None:
- """Process "get state" command and messages of combined states."""
- if ('target' in message and message['target'] == self.name and
- 'command' in message and message['command'] == 'get state'):
- await self.bus.send(Message(self.name, {'state': self.state}))
- if 'state' in message and message['sender'] in self.conf['states']:
- assert isinstance(message['sender'], str)
- assert isinstance(message['state'], bool)
- self.states[message['sender']] = message['state']
- new_state = any(self.states.values())
- if self.state != new_state:
- self.state: bool = new_state
- await self.bus.send(Message(self.name,
- {'event': 'changed',
- 'state': self.state}))
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- sends = [MessageTemplate({'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}}),
- MessageTemplate({'state': {'type': 'boolean'}})]
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'get state'}})]
- self.states: Dict[str, bool] = {}
- for state in self.conf['states']:
- receives.append(MessageTemplate({'sender': {'const': state},
- 'state': {'type': 'boolean'}}))
- self.states[state] = False
- self.state = any(self.states.values())
- self.bus.register(self.name, 'OrState',
- sends, receives, self.receive)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/controlpi_plugins/util.html b/doc/controlpi_plugins/util.html
deleted file mode 100644
index ca720bb..0000000
--- a/doc/controlpi_plugins/util.html
+++ /dev/null
@@ -1,1421 +0,0 @@
-
-
-
-
-
-
-controlpi_plugins.util API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Module controlpi_plugins.util
-
-
-Provide utility plugins for all kinds of systems.
-
-Log logs messages on stdout.
-Init sends list of messages on startup and on demand.
-Alias translates messages to an alias.
-
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test Log": {"plugin": "Log",
-... "filter": [{"sender": {"const": "Test Alias"}}]},
-... "Test Init": {"plugin": "Init",
-... "messages": [{"id": 42, "content": "Test Message"}]},
-... "Test Alias": {"plugin": "Alias",
-... "from": {"sender": {"const": "Test Init"},
-... "id": {"const": 42}},
-... "to": {"id": "translated"}}}, []))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Log', 'plugin': 'Log',
- 'sends': [], 'receives': [{'sender': {'const': 'Test Alias'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Init', 'plugin': 'Init',
- 'sends': [{'id': {'const': 42},
- 'content': {'const': 'Test Message'}}],
- 'receives': [{'target': {'const': 'Test Init'},
- 'command': {'const': 'execute'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Alias', 'plugin': 'Alias',
- 'sends': [{'id': {'const': 'translated'}}],
- 'receives': [{'sender': {'const': 'Test Init'},
- 'id': {'const': 42}}]}
-test(): {'sender': 'Test Init', 'id': 42,
- 'content': 'Test Message'}
-test(): {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Test Message'}
-Test Log: {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Test Message'}
-
-
-
-Expand source code
-
-"""Provide utility plugins for all kinds of systems.
-
-- Log logs messages on stdout.
-- Init sends list of messages on startup and on demand.
-- Alias translates messages to an alias.
-
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test Log": {"plugin": "Log",
-... "filter": [{"sender": {"const": "Test Alias"}}]},
-... "Test Init": {"plugin": "Init",
-... "messages": [{"id": 42, "content": "Test Message"}]},
-... "Test Alias": {"plugin": "Alias",
-... "from": {"sender": {"const": "Test Init"},
-... "id": {"const": 42}},
-... "to": {"id": "translated"}}}, []))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Log', 'plugin': 'Log',
- 'sends': [], 'receives': [{'sender': {'const': 'Test Alias'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Init', 'plugin': 'Init',
- 'sends': [{'id': {'const': 42},
- 'content': {'const': 'Test Message'}}],
- 'receives': [{'target': {'const': 'Test Init'},
- 'command': {'const': 'execute'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Alias', 'plugin': 'Alias',
- 'sends': [{'id': {'const': 'translated'}}],
- 'receives': [{'sender': {'const': 'Test Init'},
- 'id': {'const': 42}}]}
-test(): {'sender': 'Test Init', 'id': 42,
- 'content': 'Test Message'}
-test(): {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Test Message'}
-Test Log: {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Test Message'}
-"""
-import asyncio
-
-from controlpi import BasePlugin, Message, MessageTemplate
-
-
-class Log(BasePlugin):
- """Log messages on stdout.
-
- The "filter" configuration key gets a list of message templates defining
- the messages that should be logged by the plugin instance.
-
- In the following example the first and third message match the given
- template and are logged by the instance "Test Log", while the second
- message does not match and is only logged by the test, but not by the
- Log instance:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test Log": {"plugin": "Log",
- ... "filter": [{"id": {"const": 42}}]}},
- ... [{"id": 42, "message": "Test Message"},
- ... {"id": 42.42, "message": "Second Message"},
- ... {"id": 42, "message": "Third Message"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Log', 'plugin': 'Log',
- 'sends': [], 'receives': [{'id': {'const': 42}}]}
- test(): {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
- Test Log: {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
- test(): {'sender': 'test()', 'id': 42.42, 'message': 'Second Message'}
- test(): {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
- Test Log: {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
-
- The "filter" key is required:
- >>> asyncio.run(controlpi.test(
- ... {"Test Log": {"plugin": "Log"}}, []))
- 'filter' is a required property
- <BLANKLINE>
- Failed validating 'required' in schema:
- {'properties': {'filter': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['filter']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Log'}
- Configuration for 'Test Log' is not valid.
-
- The "filter" key has to contain a list of message templates, i.e.,
- JSON objects:
- >>> asyncio.run(controlpi.test(
- ... {"Test Log": {"plugin": "Log",
- ... "filter": [42]}}, []))
- 42 is not of type 'object'
- <BLANKLINE>
- Failed validating 'type' in schema['properties']['filter']['items']:
- {'type': 'object'}
- <BLANKLINE>
- On instance['filter'][0]:
- 42
- Configuration for 'Test Log' is not valid.
- """
-
- CONF_SCHEMA = {'properties': {'filter': {'type': 'array',
- 'items': {'type': 'object'}}},
- 'required': ['filter']}
- """Schema for Log plugin configuration.
-
- Required configuration key:
-
- - 'filter': list of message templates to be logged.
- """
-
- async def log(self, message: Message) -> None:
- """Log received message on stdout using own name as prefix."""
- print(f"{self.name}: {message}")
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.bus.register(self.name, 'Log', [], self.conf['filter'], self.log)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-class Init(BasePlugin):
- """Send list of messages on startup and on demand.
-
- The "messages" configuration key gets a list of messages to be sent on
- startup. The same list is sent in reaction to a message with
- "target": <name> and "command": "execute".
-
- In the example, the two configured messages are sent twice, once at
- startup and a second time in reaction to the "execute" command sent by
- the test:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test Init": {"plugin": "Init",
- ... "messages": [{"id": 42,
- ... "content": "Test Message"},
- ... {"id": 42.42,
- ... "content": "Second Message"}]}},
- ... [{"target": "Test Init", "command": "execute"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Init', 'plugin': 'Init',
- 'sends': [{'id': {'const': 42},
- 'content': {'const': 'Test Message'}},
- {'id': {'const': 42.42},
- 'content': {'const': 'Second Message'}}],
- 'receives': [{'target': {'const': 'Test Init'},
- 'command': {'const': 'execute'}}]}
- test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
- test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
- test(): {'sender': 'test()', 'target': 'Test Init', 'command': 'execute'}
- test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
- test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
-
- The "messages" key is required:
- >>> asyncio.run(controlpi.test(
- ... {"Test Init": {"plugin": "Init"}}, []))
- 'messages' is a required property
- <BLANKLINE>
- Failed validating 'required' in schema:
- {'properties': {'messages': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['messages']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Init'}
- Configuration for 'Test Init' is not valid.
-
- The "messages" key has to contain a list of (partial) messages, i.e.,
- JSON objects:
- >>> asyncio.run(controlpi.test(
- ... {"Test Init": {"plugin": "Init",
- ... "messages": [42]}}, []))
- 42 is not of type 'object'
- <BLANKLINE>
- Failed validating 'type' in schema['properties']['messages']['items']:
- {'type': 'object'}
- <BLANKLINE>
- On instance['messages'][0]:
- 42
- Configuration for 'Test Init' is not valid.
- """
-
- CONF_SCHEMA = {'properties': {'messages': {'type': 'array',
- 'items': {'type': 'object'}}},
- 'required': ['messages']}
- """Schema for Init plugin configuration.
-
- Required configuration key:
-
- - 'messages': list of messages to be sent.
- """
-
- async def execute(self, message: Message) -> None:
- """Send configured messages."""
- for message in self.conf['messages']:
- await self.bus.send(Message(self.name, message))
- # Give immediate reactions to messages opportunity to happen:
- await asyncio.sleep(0)
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'execute'}})]
- sends = [MessageTemplate.from_message(message)
- for message in self.conf['messages']]
- self.bus.register(self.name, 'Init', sends, receives, self.execute)
-
- async def run(self) -> None:
- """Send configured messages on startup."""
- for message in self.conf['messages']:
- await self.bus.send(Message(self.name, message))
-
-
-class Execute(BasePlugin):
- """Send configurable list of messages on demand.
-
- An Execute plugin instance receives two kinds of commands.
- The "set messages" command has a "messages" key with a list of (partial)
- messages, which are sent by the Execute instance in reaction to an
- "execute" command.
-
- In the example, the first command sent by the test sets two messages,
- which are then sent in reaction to the second command sent by the test:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test Execute": {"plugin": "Execute"}},
- ... [{"target": "Test Execute", "command": "set messages",
- ... "messages": [{"id": 42, "content": "Test Message"},
- ... {"id": 42.42, "content": "Second Message"}]},
- ... {"target": "Test Execute", "command": "execute"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Execute', 'plugin': 'Execute',
- 'sends': [{}],
- 'receives': [{'target': {'const': 'Test Execute'},
- 'command': {'const': 'set messages'},
- 'messages': {'type': 'array',
- 'items': {'type': 'object'}}},
- {'target': {'const': 'Test Execute'},
- 'command': {'const': 'execute'}}]}
- test(): {'sender': 'test()', 'target': 'Test Execute',
- 'command': 'set messages',
- 'messages': [{'id': 42, 'content': 'Test Message'},
- {'id': 42.42, 'content': 'Second Message'}]}
- test(): {'sender': 'test()', 'target': 'Test Execute',
- 'command': 'execute'}
- test(): {'sender': 'Test Execute', 'id': 42,
- 'content': 'Test Message'}
- test(): {'sender': 'Test Execute', 'id': 42.42,
- 'content': 'Second Message'}
- """
-
- CONF_SCHEMA = True
- """Schema for Execute plugin configuration.
-
- There are no required or optional configuration keys.
- """
-
- async def execute(self, message: Message) -> None:
- """Set or send configured messages."""
- if message['command'] == 'set messages':
- assert isinstance(message['messages'], list)
- self.messages = list(message['messages'])
- elif message['command'] == 'execute':
- for message in self.messages:
- await self.bus.send(Message(self.name, message))
- # Give immediate reactions to messages opportunity to happen:
- await asyncio.sleep(0)
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.messages = []
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set messages'},
- 'messages':
- {'type': 'array',
- 'items': {'type': 'object'}}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'execute'}})]
- sends = [MessageTemplate()]
- self.bus.register(self.name, 'Execute', sends, receives, self.execute)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-class Alias(BasePlugin):
- """Translate messages to an alias.
-
- The "from" configuration key gets a message template and the
- configuration key "to" a (partial) message. All messages matching the
- template are received by the Alias instance and a message translated by
- removing the keys of the "from" template and adding the keys and values
- of the "to" message is sent. Keys that are neither in "from" nor in "to"
- are retained.
-
- In the example, the two messages sent by the test are translated by the
- Alias instance and the translated messages are sent by it preserving
- the "content" keys:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test Alias": {"plugin": "Alias",
- ... "from": {"id": {"const": 42}},
- ... "to": {"id": "translated"}}},
- ... [{"id": 42, "content": "Test Message"},
- ... {"id": 42, "content": "Second Message"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Alias', 'plugin': 'Alias',
- 'sends': [{'id': {'const': 'translated'}}],
- 'receives': [{'id': {'const': 42}}]}
- test(): {'sender': 'test()', 'id': 42,
- 'content': 'Test Message'}
- test(): {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Test Message'}
- test(): {'sender': 'test()', 'id': 42,
- 'content': 'Second Message'}
- test(): {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Second Message'}
-
- The "from" and "to" keys are required:
- >>> asyncio.run(controlpi.test(
- ... {"Test Alias": {"plugin": "Alias"}}, []))
- 'from' is a required property
- <BLANKLINE>
- Failed validating 'required' in schema:
- {'properties': {'from': {'type': 'object'}, 'to': {'type': 'object'}},
- 'required': ['from', 'to']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Alias'}
- 'to' is a required property
- <BLANKLINE>
- Failed validating 'required' in schema:
- {'properties': {'from': {'type': 'object'}, 'to': {'type': 'object'}},
- 'required': ['from', 'to']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Alias'}
- Configuration for 'Test Alias' is not valid.
-
- The "from" key has to contain a message template and the "to" key a
- (partial) message, i.e., both have to be JSON objects:
- >>> asyncio.run(controlpi.test(
- ... {"Test Alias": {"plugin": "Alias",
- ... "from": 42,
- ... "to": 42}}, []))
- 42 is not of type 'object'
- <BLANKLINE>
- Failed validating 'type' in schema['properties']['from']:
- {'type': 'object'}
- <BLANKLINE>
- On instance['from']:
- 42
- 42 is not of type 'object'
- <BLANKLINE>
- Failed validating 'type' in schema['properties']['to']:
- {'type': 'object'}
- <BLANKLINE>
- On instance['to']:
- 42
- Configuration for 'Test Alias' is not valid.
- """
-
- CONF_SCHEMA = {'properties': {'from': {'type': 'object'},
- 'to': {'type': 'object'}},
- 'required': ['from', 'to']}
- """Schema for Alias plugin configuration.
-
- Required configuration keys:
-
- - 'from': template of messages to be translated.
- - 'to': translated message to be sent.
- """
-
- async def alias(self, message: Message) -> None:
- """Translate and send message."""
- alias_message = Message(self.name)
- alias_message.update(self.conf['to'])
- for key in message:
- if key != 'sender' and key not in self.conf['from']:
- alias_message[key] = message[key]
- await self.bus.send(alias_message)
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.bus.register(self.name, 'Alias',
- [MessageTemplate.from_message(self.conf['to'])],
- [self.conf['from']],
- self.alias)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-
-
-
-
-class Log
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Log messages on stdout.
-
The "filter" configuration key gets a list of message templates defining
-the messages that should be logged by the plugin instance.
-
In the following example the first and third message match the given
-template and are logged by the instance "Test Log", while the second
-message does not match and is only logged by the test, but not by the
-Log instance:
-
>>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test Log": {"plugin": "Log",
-... "filter": [{"id": {"const": 42}}]}},
-... [{"id": 42, "message": "Test Message"},
-... {"id": 42.42, "message": "Second Message"},
-... {"id": 42, "message": "Third Message"}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Log', 'plugin': 'Log',
- 'sends': [], 'receives': [{'id': {'const': 42}}]}
-test(): {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
-Test Log: {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
-test(): {'sender': 'test()', 'id': 42.42, 'message': 'Second Message'}
-test(): {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
-Test Log: {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
-
-
The "filter" key is required:
-
>>> asyncio.run(controlpi.test(
-... {"Test Log": {"plugin": "Log"}}, []))
-'filter' is a required property
-<BLANKLINE>
-Failed validating 'required' in schema:
- {'properties': {'filter': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['filter']}
-<BLANKLINE>
-On instance:
- {'plugin': 'Log'}
-Configuration for 'Test Log' is not valid.
-
-
The "filter" key has to contain a list of message templates, i.e.,
-JSON objects:
-
>>> asyncio.run(controlpi.test(
-... {"Test Log": {"plugin": "Log",
-... "filter": [42]}}, []))
-42 is not of type 'object'
-<BLANKLINE>
-Failed validating 'type' in schema['properties']['filter']['items']:
- {'type': 'object'}
-<BLANKLINE>
-On instance['filter'][0]:
- 42
-Configuration for 'Test Log' is not valid.
-
-
-
-Expand source code
-
-class Log(BasePlugin):
- """Log messages on stdout.
-
- The "filter" configuration key gets a list of message templates defining
- the messages that should be logged by the plugin instance.
-
- In the following example the first and third message match the given
- template and are logged by the instance "Test Log", while the second
- message does not match and is only logged by the test, but not by the
- Log instance:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test Log": {"plugin": "Log",
- ... "filter": [{"id": {"const": 42}}]}},
- ... [{"id": 42, "message": "Test Message"},
- ... {"id": 42.42, "message": "Second Message"},
- ... {"id": 42, "message": "Third Message"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Log', 'plugin': 'Log',
- 'sends': [], 'receives': [{'id': {'const': 42}}]}
- test(): {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
- Test Log: {'sender': 'test()', 'id': 42, 'message': 'Test Message'}
- test(): {'sender': 'test()', 'id': 42.42, 'message': 'Second Message'}
- test(): {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
- Test Log: {'sender': 'test()', 'id': 42, 'message': 'Third Message'}
-
- The "filter" key is required:
- >>> asyncio.run(controlpi.test(
- ... {"Test Log": {"plugin": "Log"}}, []))
- 'filter' is a required property
- <BLANKLINE>
- Failed validating 'required' in schema:
- {'properties': {'filter': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['filter']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Log'}
- Configuration for 'Test Log' is not valid.
-
- The "filter" key has to contain a list of message templates, i.e.,
- JSON objects:
- >>> asyncio.run(controlpi.test(
- ... {"Test Log": {"plugin": "Log",
- ... "filter": [42]}}, []))
- 42 is not of type 'object'
- <BLANKLINE>
- Failed validating 'type' in schema['properties']['filter']['items']:
- {'type': 'object'}
- <BLANKLINE>
- On instance['filter'][0]:
- 42
- Configuration for 'Test Log' is not valid.
- """
-
- CONF_SCHEMA = {'properties': {'filter': {'type': 'array',
- 'items': {'type': 'object'}}},
- 'required': ['filter']}
- """Schema for Log plugin configuration.
-
- Required configuration key:
-
- - 'filter': list of message templates to be logged.
- """
-
- async def log(self, message: Message) -> None:
- """Log received message on stdout using own name as prefix."""
- print(f"{self.name}: {message}")
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.bus.register(self.name, 'Log', [], self.conf['filter'], self.log)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for Log plugin configuration.
-
Required configuration key:
-
-'filter': list of message templates to be logged.
-
-
-
-Methods
-
-
-async def log (self, message:Â Message ) â>Â NoneType
-
-
-Log received message on stdout using own name as prefix.
-
-
-Expand source code
-
-async def log(self, message: Message) -> None:
- """Log received message on stdout using own name as prefix."""
- print(f"{self.name}: {message}")
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.bus.register(self.name, 'Log', [], self.conf['filter'], self.log)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-class Init
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Send list of messages on startup and on demand.
-
The "messages" configuration key gets a list of messages to be sent on
-startup. The same list is sent in reaction to a message with
-"target": and "command": "execute".
-
In the example, the two configured messages are sent twice, once at
-startup and a second time in reaction to the "execute" command sent by
-the test:
-
>>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test Init": {"plugin": "Init",
-... "messages": [{"id": 42,
-... "content": "Test Message"},
-... {"id": 42.42,
-... "content": "Second Message"}]}},
-... [{"target": "Test Init", "command": "execute"}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Init', 'plugin': 'Init',
- 'sends': [{'id': {'const': 42},
- 'content': {'const': 'Test Message'}},
- {'id': {'const': 42.42},
- 'content': {'const': 'Second Message'}}],
- 'receives': [{'target': {'const': 'Test Init'},
- 'command': {'const': 'execute'}}]}
-test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
-test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
-test(): {'sender': 'test()', 'target': 'Test Init', 'command': 'execute'}
-test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
-test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
-
-
The "messages" key is required:
-
>>> asyncio.run(controlpi.test(
-... {"Test Init": {"plugin": "Init"}}, []))
-'messages' is a required property
-<BLANKLINE>
-Failed validating 'required' in schema:
- {'properties': {'messages': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['messages']}
-<BLANKLINE>
-On instance:
- {'plugin': 'Init'}
-Configuration for 'Test Init' is not valid.
-
-
The "messages" key has to contain a list of (partial) messages, i.e.,
-JSON objects:
-
>>> asyncio.run(controlpi.test(
-... {"Test Init": {"plugin": "Init",
-... "messages": [42]}}, []))
-42 is not of type 'object'
-<BLANKLINE>
-Failed validating 'type' in schema['properties']['messages']['items']:
- {'type': 'object'}
-<BLANKLINE>
-On instance['messages'][0]:
- 42
-Configuration for 'Test Init' is not valid.
-
-
-
-Expand source code
-
-class Init(BasePlugin):
- """Send list of messages on startup and on demand.
-
- The "messages" configuration key gets a list of messages to be sent on
- startup. The same list is sent in reaction to a message with
- "target": <name> and "command": "execute".
-
- In the example, the two configured messages are sent twice, once at
- startup and a second time in reaction to the "execute" command sent by
- the test:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test Init": {"plugin": "Init",
- ... "messages": [{"id": 42,
- ... "content": "Test Message"},
- ... {"id": 42.42,
- ... "content": "Second Message"}]}},
- ... [{"target": "Test Init", "command": "execute"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Init', 'plugin': 'Init',
- 'sends': [{'id': {'const': 42},
- 'content': {'const': 'Test Message'}},
- {'id': {'const': 42.42},
- 'content': {'const': 'Second Message'}}],
- 'receives': [{'target': {'const': 'Test Init'},
- 'command': {'const': 'execute'}}]}
- test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
- test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
- test(): {'sender': 'test()', 'target': 'Test Init', 'command': 'execute'}
- test(): {'sender': 'Test Init', 'id': 42, 'content': 'Test Message'}
- test(): {'sender': 'Test Init', 'id': 42.42, 'content': 'Second Message'}
-
- The "messages" key is required:
- >>> asyncio.run(controlpi.test(
- ... {"Test Init": {"plugin": "Init"}}, []))
- 'messages' is a required property
- <BLANKLINE>
- Failed validating 'required' in schema:
- {'properties': {'messages': {'items': {'type': 'object'},
- 'type': 'array'}},
- 'required': ['messages']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Init'}
- Configuration for 'Test Init' is not valid.
-
- The "messages" key has to contain a list of (partial) messages, i.e.,
- JSON objects:
- >>> asyncio.run(controlpi.test(
- ... {"Test Init": {"plugin": "Init",
- ... "messages": [42]}}, []))
- 42 is not of type 'object'
- <BLANKLINE>
- Failed validating 'type' in schema['properties']['messages']['items']:
- {'type': 'object'}
- <BLANKLINE>
- On instance['messages'][0]:
- 42
- Configuration for 'Test Init' is not valid.
- """
-
- CONF_SCHEMA = {'properties': {'messages': {'type': 'array',
- 'items': {'type': 'object'}}},
- 'required': ['messages']}
- """Schema for Init plugin configuration.
-
- Required configuration key:
-
- - 'messages': list of messages to be sent.
- """
-
- async def execute(self, message: Message) -> None:
- """Send configured messages."""
- for message in self.conf['messages']:
- await self.bus.send(Message(self.name, message))
- # Give immediate reactions to messages opportunity to happen:
- await asyncio.sleep(0)
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'execute'}})]
- sends = [MessageTemplate.from_message(message)
- for message in self.conf['messages']]
- self.bus.register(self.name, 'Init', sends, receives, self.execute)
-
- async def run(self) -> None:
- """Send configured messages on startup."""
- for message in self.conf['messages']:
- await self.bus.send(Message(self.name, message))
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for Init plugin configuration.
-
Required configuration key:
-
-'messages': list of messages to be sent.
-
-
-
-Methods
-
-
-async def execute (self, message:Â Message ) â>Â NoneType
-
-
-Send configured messages.
-
-
-Expand source code
-
-async def execute(self, message: Message) -> None:
- """Send configured messages."""
- for message in self.conf['messages']:
- await self.bus.send(Message(self.name, message))
- # Give immediate reactions to messages opportunity to happen:
- await asyncio.sleep(0)
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'execute'}})]
- sends = [MessageTemplate.from_message(message)
- for message in self.conf['messages']]
- self.bus.register(self.name, 'Init', sends, receives, self.execute)
-
-
-
-async def run (self) â>Â NoneType
-
-
-Send configured messages on startup.
-
-
-Expand source code
-
-async def run(self) -> None:
- """Send configured messages on startup."""
- for message in self.conf['messages']:
- await self.bus.send(Message(self.name, message))
-
-
-
-
-
-class Execute
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Send configurable list of messages on demand.
-
An Execute plugin instance receives two kinds of commands.
-The "set messages" command has a "messages" key with a list of (partial)
-messages, which are sent by the Execute instance in reaction to an
-"execute" command.
-
In the example, the first command sent by the test sets two messages,
-which are then sent in reaction to the second command sent by the test:
-
>>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test Execute": {"plugin": "Execute"}},
-... [{"target": "Test Execute", "command": "set messages",
-... "messages": [{"id": 42, "content": "Test Message"},
-... {"id": 42.42, "content": "Second Message"}]},
-... {"target": "Test Execute", "command": "execute"}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Execute', 'plugin': 'Execute',
- 'sends': [{}],
- 'receives': [{'target': {'const': 'Test Execute'},
- 'command': {'const': 'set messages'},
- 'messages': {'type': 'array',
- 'items': {'type': 'object'}}},
- {'target': {'const': 'Test Execute'},
- 'command': {'const': 'execute'}}]}
-test(): {'sender': 'test()', 'target': 'Test Execute',
- 'command': 'set messages',
- 'messages': [{'id': 42, 'content': 'Test Message'},
- {'id': 42.42, 'content': 'Second Message'}]}
-test(): {'sender': 'test()', 'target': 'Test Execute',
- 'command': 'execute'}
-test(): {'sender': 'Test Execute', 'id': 42,
- 'content': 'Test Message'}
-test(): {'sender': 'Test Execute', 'id': 42.42,
- 'content': 'Second Message'}
-
-
-
-Expand source code
-
-class Execute(BasePlugin):
- """Send configurable list of messages on demand.
-
- An Execute plugin instance receives two kinds of commands.
- The "set messages" command has a "messages" key with a list of (partial)
- messages, which are sent by the Execute instance in reaction to an
- "execute" command.
-
- In the example, the first command sent by the test sets two messages,
- which are then sent in reaction to the second command sent by the test:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test Execute": {"plugin": "Execute"}},
- ... [{"target": "Test Execute", "command": "set messages",
- ... "messages": [{"id": 42, "content": "Test Message"},
- ... {"id": 42.42, "content": "Second Message"}]},
- ... {"target": "Test Execute", "command": "execute"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Execute', 'plugin': 'Execute',
- 'sends': [{}],
- 'receives': [{'target': {'const': 'Test Execute'},
- 'command': {'const': 'set messages'},
- 'messages': {'type': 'array',
- 'items': {'type': 'object'}}},
- {'target': {'const': 'Test Execute'},
- 'command': {'const': 'execute'}}]}
- test(): {'sender': 'test()', 'target': 'Test Execute',
- 'command': 'set messages',
- 'messages': [{'id': 42, 'content': 'Test Message'},
- {'id': 42.42, 'content': 'Second Message'}]}
- test(): {'sender': 'test()', 'target': 'Test Execute',
- 'command': 'execute'}
- test(): {'sender': 'Test Execute', 'id': 42,
- 'content': 'Test Message'}
- test(): {'sender': 'Test Execute', 'id': 42.42,
- 'content': 'Second Message'}
- """
-
- CONF_SCHEMA = True
- """Schema for Execute plugin configuration.
-
- There are no required or optional configuration keys.
- """
-
- async def execute(self, message: Message) -> None:
- """Set or send configured messages."""
- if message['command'] == 'set messages':
- assert isinstance(message['messages'], list)
- self.messages = list(message['messages'])
- elif message['command'] == 'execute':
- for message in self.messages:
- await self.bus.send(Message(self.name, message))
- # Give immediate reactions to messages opportunity to happen:
- await asyncio.sleep(0)
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.messages = []
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set messages'},
- 'messages':
- {'type': 'array',
- 'items': {'type': 'object'}}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'execute'}})]
- sends = [MessageTemplate()]
- self.bus.register(self.name, 'Execute', sends, receives, self.execute)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for Execute plugin configuration.
-
There are no required or optional configuration keys.
-
-
-Methods
-
-
-async def execute (self, message:Â Message ) â>Â NoneType
-
-
-Set or send configured messages.
-
-
-Expand source code
-
-async def execute(self, message: Message) -> None:
- """Set or send configured messages."""
- if message['command'] == 'set messages':
- assert isinstance(message['messages'], list)
- self.messages = list(message['messages'])
- elif message['command'] == 'execute':
- for message in self.messages:
- await self.bus.send(Message(self.name, message))
- # Give immediate reactions to messages opportunity to happen:
- await asyncio.sleep(0)
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.messages = []
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'set messages'},
- 'messages':
- {'type': 'array',
- 'items': {'type': 'object'}}}),
- MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'execute'}})]
- sends = [MessageTemplate()]
- self.bus.register(self.name, 'Execute', sends, receives, self.execute)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-class Alias
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Translate messages to an alias.
-
The "from" configuration key gets a message template and the
-configuration key "to" a (partial) message. All messages matching the
-template are received by the Alias instance and a message translated by
-removing the keys of the "from" template and adding the keys and values
-of the "to" message is sent. Keys that are neither in "from" nor in "to"
-are retained.
-
In the example, the two messages sent by the test are translated by the
-Alias instance and the translated messages are sent by it preserving
-the "content" keys:
-
>>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test Alias": {"plugin": "Alias",
-... "from": {"id": {"const": 42}},
-... "to": {"id": "translated"}}},
-... [{"id": 42, "content": "Test Message"},
-... {"id": 42, "content": "Second Message"}]))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Alias', 'plugin': 'Alias',
- 'sends': [{'id': {'const': 'translated'}}],
- 'receives': [{'id': {'const': 42}}]}
-test(): {'sender': 'test()', 'id': 42,
- 'content': 'Test Message'}
-test(): {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Test Message'}
-test(): {'sender': 'test()', 'id': 42,
- 'content': 'Second Message'}
-test(): {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Second Message'}
-
-
The "from" and "to" keys are required:
-
>>> asyncio.run(controlpi.test(
-... {"Test Alias": {"plugin": "Alias"}}, []))
-'from' is a required property
-<BLANKLINE>
-Failed validating 'required' in schema:
- {'properties': {'from': {'type': 'object'}, 'to': {'type': 'object'}},
- 'required': ['from', 'to']}
-<BLANKLINE>
-On instance:
- {'plugin': 'Alias'}
-'to' is a required property
-<BLANKLINE>
-Failed validating 'required' in schema:
- {'properties': {'from': {'type': 'object'}, 'to': {'type': 'object'}},
- 'required': ['from', 'to']}
-<BLANKLINE>
-On instance:
- {'plugin': 'Alias'}
-Configuration for 'Test Alias' is not valid.
-
-
The "from" key has to contain a message template and the "to" key a
-(partial) message, i.e., both have to be JSON objects:
-
>>> asyncio.run(controlpi.test(
-... {"Test Alias": {"plugin": "Alias",
-... "from": 42,
-... "to": 42}}, []))
-42 is not of type 'object'
-<BLANKLINE>
-Failed validating 'type' in schema['properties']['from']:
- {'type': 'object'}
-<BLANKLINE>
-On instance['from']:
- 42
-42 is not of type 'object'
-<BLANKLINE>
-Failed validating 'type' in schema['properties']['to']:
- {'type': 'object'}
-<BLANKLINE>
-On instance['to']:
- 42
-Configuration for 'Test Alias' is not valid.
-
-
-
-Expand source code
-
-class Alias(BasePlugin):
- """Translate messages to an alias.
-
- The "from" configuration key gets a message template and the
- configuration key "to" a (partial) message. All messages matching the
- template are received by the Alias instance and a message translated by
- removing the keys of the "from" template and adding the keys and values
- of the "to" message is sent. Keys that are neither in "from" nor in "to"
- are retained.
-
- In the example, the two messages sent by the test are translated by the
- Alias instance and the translated messages are sent by it preserving
- the "content" keys:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test Alias": {"plugin": "Alias",
- ... "from": {"id": {"const": 42}},
- ... "to": {"id": "translated"}}},
- ... [{"id": 42, "content": "Test Message"},
- ... {"id": 42, "content": "Second Message"}]))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Alias', 'plugin': 'Alias',
- 'sends': [{'id': {'const': 'translated'}}],
- 'receives': [{'id': {'const': 42}}]}
- test(): {'sender': 'test()', 'id': 42,
- 'content': 'Test Message'}
- test(): {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Test Message'}
- test(): {'sender': 'test()', 'id': 42,
- 'content': 'Second Message'}
- test(): {'sender': 'Test Alias', 'id': 'translated',
- 'content': 'Second Message'}
-
- The "from" and "to" keys are required:
- >>> asyncio.run(controlpi.test(
- ... {"Test Alias": {"plugin": "Alias"}}, []))
- 'from' is a required property
- <BLANKLINE>
- Failed validating 'required' in schema:
- {'properties': {'from': {'type': 'object'}, 'to': {'type': 'object'}},
- 'required': ['from', 'to']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Alias'}
- 'to' is a required property
- <BLANKLINE>
- Failed validating 'required' in schema:
- {'properties': {'from': {'type': 'object'}, 'to': {'type': 'object'}},
- 'required': ['from', 'to']}
- <BLANKLINE>
- On instance:
- {'plugin': 'Alias'}
- Configuration for 'Test Alias' is not valid.
-
- The "from" key has to contain a message template and the "to" key a
- (partial) message, i.e., both have to be JSON objects:
- >>> asyncio.run(controlpi.test(
- ... {"Test Alias": {"plugin": "Alias",
- ... "from": 42,
- ... "to": 42}}, []))
- 42 is not of type 'object'
- <BLANKLINE>
- Failed validating 'type' in schema['properties']['from']:
- {'type': 'object'}
- <BLANKLINE>
- On instance['from']:
- 42
- 42 is not of type 'object'
- <BLANKLINE>
- Failed validating 'type' in schema['properties']['to']:
- {'type': 'object'}
- <BLANKLINE>
- On instance['to']:
- 42
- Configuration for 'Test Alias' is not valid.
- """
-
- CONF_SCHEMA = {'properties': {'from': {'type': 'object'},
- 'to': {'type': 'object'}},
- 'required': ['from', 'to']}
- """Schema for Alias plugin configuration.
-
- Required configuration keys:
-
- - 'from': template of messages to be translated.
- - 'to': translated message to be sent.
- """
-
- async def alias(self, message: Message) -> None:
- """Translate and send message."""
- alias_message = Message(self.name)
- alias_message.update(self.conf['to'])
- for key in message:
- if key != 'sender' and key not in self.conf['from']:
- alias_message[key] = message[key]
- await self.bus.send(alias_message)
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.bus.register(self.name, 'Alias',
- [MessageTemplate.from_message(self.conf['to'])],
- [self.conf['from']],
- self.alias)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for Alias plugin configuration.
-
Required configuration keys:
-
-'from': template of messages to be translated.
-'to': translated message to be sent.
-
-
-
-Methods
-
-
-async def alias (self, message:Â Message ) â>Â NoneType
-
-
-Translate and send message.
-
-
-Expand source code
-
-async def alias(self, message: Message) -> None:
- """Translate and send message."""
- alias_message = Message(self.name)
- alias_message.update(self.conf['to'])
- for key in message:
- if key != 'sender' and key not in self.conf['from']:
- alias_message[key] = message[key]
- await self.bus.send(alias_message)
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- self.bus.register(self.name, 'Alias',
- [MessageTemplate.from_message(self.conf['to'])],
- [self.conf['from']],
- self.alias)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/controlpi_plugins/wait.html b/doc/controlpi_plugins/wait.html
deleted file mode 100644
index a5230a5..0000000
--- a/doc/controlpi_plugins/wait.html
+++ /dev/null
@@ -1,604 +0,0 @@
-
-
-
-
-
-
-controlpi_plugins.wait API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Module controlpi_plugins.wait
-
-
-Provide waiting/sleeping plugins for all kinds of systems.
-
-Wait waits for time defined in configuration and sends "finished" event.
-GenericWait waits for time defined in "wait" command and sends "finished"
-event with "id" string defined in "wait" command.
-
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test Wait": {"plugin": "Wait", "seconds": 0.01},
-... "Test GenericWait": {"plugin": "GenericWait"}},
-... [{"target": "Test GenericWait", "command": "wait",
-... "seconds": 0.02, "id": "Long Wait"},
-... {"target": "Test Wait", "command": "wait"}], 0.025))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Wait', 'plugin': 'Wait',
- 'sends': [{'event': {'const': 'finished'}}],
- 'receives': [{'target': {'const': 'Test Wait'},
- 'command': {'const': 'wait'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test GenericWait', 'plugin': 'GenericWait',
- 'sends': [{'event': {'const': 'finished'},
- 'id': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Test GenericWait'},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}}]}
-test(): {'sender': 'test()', 'target': 'Test GenericWait',
- 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
-test(): {'sender': 'test()', 'target': 'Test Wait', 'command': 'wait'}
-test(): {'sender': 'Test Wait', 'event': 'finished'}
-test(): {'sender': 'Test GenericWait', 'event': 'finished',
- 'id': 'Long Wait'}
-
-
-
-Expand source code
-
-"""Provide waiting/sleeping plugins for all kinds of systems.
-
-- Wait waits for time defined in configuration and sends "finished" event.
-- GenericWait waits for time defined in "wait" command and sends "finished"
- event with "id" string defined in "wait" command.
-
->>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test Wait": {"plugin": "Wait", "seconds": 0.01},
-... "Test GenericWait": {"plugin": "GenericWait"}},
-... [{"target": "Test GenericWait", "command": "wait",
-... "seconds": 0.02, "id": "Long Wait"},
-... {"target": "Test Wait", "command": "wait"}], 0.025))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test Wait', 'plugin': 'Wait',
- 'sends': [{'event': {'const': 'finished'}}],
- 'receives': [{'target': {'const': 'Test Wait'},
- 'command': {'const': 'wait'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test GenericWait', 'plugin': 'GenericWait',
- 'sends': [{'event': {'const': 'finished'},
- 'id': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Test GenericWait'},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}}]}
-test(): {'sender': 'test()', 'target': 'Test GenericWait',
- 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
-test(): {'sender': 'test()', 'target': 'Test Wait', 'command': 'wait'}
-test(): {'sender': 'Test Wait', 'event': 'finished'}
-test(): {'sender': 'Test GenericWait', 'event': 'finished',
- 'id': 'Long Wait'}
-"""
-import asyncio
-
-from controlpi import BasePlugin, Message, MessageTemplate
-
-
-class Wait(BasePlugin):
- """Wait for time defined in configuration.
-
- The "seconds" configuration key gets the number of seconds to wait after
- receiving a "wait" command before sending the "finished" event:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Long Wait": {"plugin": "Wait", "seconds": 0.02},
- ... "Short Wait": {"plugin": "Wait", "seconds": 0.01}},
- ... [{"target": "Long Wait", "command": "wait"},
- ... {"target": "Short Wait", "command": "wait"}], 0.025))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Long Wait', 'plugin': 'Wait',
- 'sends': [{'event': {'const': 'finished'}}],
- 'receives': [{'target': {'const': 'Long Wait'},
- 'command': {'const': 'wait'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Short Wait', 'plugin': 'Wait',
- 'sends': [{'event': {'const': 'finished'}}],
- 'receives': [{'target': {'const': 'Short Wait'},
- 'command': {'const': 'wait'}}]}
- test(): {'sender': 'test()', 'target': 'Long Wait', 'command': 'wait'}
- test(): {'sender': 'test()', 'target': 'Short Wait', 'command': 'wait'}
- test(): {'sender': 'Short Wait', 'event': 'finished'}
- test(): {'sender': 'Long Wait', 'event': 'finished'}
- """
-
- CONF_SCHEMA = {'properties': {'seconds': {'type': 'number'}},
- 'required': ['seconds']}
- """Schema for Wait plugin configuration.
-
- Required configuration key:
-
- - 'seconds': number of seconds to wait.
- """
-
- async def wait(self, message: Message) -> None:
- """Wait configured time and send "finished" event."""
- async def wait_coroutine():
- await asyncio.sleep(self.conf['seconds'])
- await self.bus.send(Message(self.name, {'event': 'finished'}))
- # Done in separate task to not block queue awaiting this callback:
- asyncio.create_task(wait_coroutine())
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'wait'}})]
- sends = [MessageTemplate({'event': {'const': 'finished'}})]
- self.bus.register(self.name, 'Wait', sends, receives, self.wait)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-class GenericWait(BasePlugin):
- """Wait for time defined in "wait" command.
-
- The "wait" command has message keys "seconds" defining the seconds to
- wait and "id" defining a string to be sent back in the "finished" event
- after the wait:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test GenericWait": {"plugin": "GenericWait"}},
- ... [{"target": "Test GenericWait", "command": "wait",
- ... "seconds": 0.02, "id": "Long Wait"},
- ... {"target": "Test GenericWait", "command": "wait",
- ... "seconds": 0.01, "id": "Short Wait"}], 0.025))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test GenericWait', 'plugin': 'GenericWait',
- 'sends': [{'event': {'const': 'finished'},
- 'id': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Test GenericWait'},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}}]}
- test(): {'sender': 'test()', 'target': 'Test GenericWait',
- 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
- test(): {'sender': 'test()', 'target': 'Test GenericWait',
- 'command': 'wait', 'seconds': 0.01, 'id': 'Short Wait'}
- test(): {'sender': 'Test GenericWait', 'event': 'finished',
- 'id': 'Short Wait'}
- test(): {'sender': 'Test GenericWait', 'event': 'finished',
- 'id': 'Long Wait'}
- """
-
- CONF_SCHEMA = True
- """Schema for GenericWait plugin configuration.
-
- There are no required or optional configuration keys.
- """
-
- async def wait(self, message: Message) -> None:
- """Wait given time and send "finished" event with given "id"."""
- async def wait_coroutine():
- assert isinstance(message['seconds'], float)
- await asyncio.sleep(message['seconds'])
- await self.bus.send(Message(self.name, {'event': 'finished',
- 'id': message['id']}))
- # Done in separate task to not block queue awaiting this callback:
- asyncio.create_task(wait_coroutine())
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}})]
- sends = [MessageTemplate({'event': {'const': 'finished'},
- 'id': {'type': 'string'}})]
- self.bus.register(self.name, 'GenericWait', sends, receives, self.wait)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-
-
-
-
-class Wait
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Wait for time defined in configuration.
-
The "seconds" configuration key gets the number of seconds to wait after
-receiving a "wait" command before sending the "finished" event:
-
>>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Long Wait": {"plugin": "Wait", "seconds": 0.02},
-... "Short Wait": {"plugin": "Wait", "seconds": 0.01}},
-... [{"target": "Long Wait", "command": "wait"},
-... {"target": "Short Wait", "command": "wait"}], 0.025))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Long Wait', 'plugin': 'Wait',
- 'sends': [{'event': {'const': 'finished'}}],
- 'receives': [{'target': {'const': 'Long Wait'},
- 'command': {'const': 'wait'}}]}
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Short Wait', 'plugin': 'Wait',
- 'sends': [{'event': {'const': 'finished'}}],
- 'receives': [{'target': {'const': 'Short Wait'},
- 'command': {'const': 'wait'}}]}
-test(): {'sender': 'test()', 'target': 'Long Wait', 'command': 'wait'}
-test(): {'sender': 'test()', 'target': 'Short Wait', 'command': 'wait'}
-test(): {'sender': 'Short Wait', 'event': 'finished'}
-test(): {'sender': 'Long Wait', 'event': 'finished'}
-
-
-
-Expand source code
-
-class Wait(BasePlugin):
- """Wait for time defined in configuration.
-
- The "seconds" configuration key gets the number of seconds to wait after
- receiving a "wait" command before sending the "finished" event:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Long Wait": {"plugin": "Wait", "seconds": 0.02},
- ... "Short Wait": {"plugin": "Wait", "seconds": 0.01}},
- ... [{"target": "Long Wait", "command": "wait"},
- ... {"target": "Short Wait", "command": "wait"}], 0.025))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Long Wait', 'plugin': 'Wait',
- 'sends': [{'event': {'const': 'finished'}}],
- 'receives': [{'target': {'const': 'Long Wait'},
- 'command': {'const': 'wait'}}]}
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Short Wait', 'plugin': 'Wait',
- 'sends': [{'event': {'const': 'finished'}}],
- 'receives': [{'target': {'const': 'Short Wait'},
- 'command': {'const': 'wait'}}]}
- test(): {'sender': 'test()', 'target': 'Long Wait', 'command': 'wait'}
- test(): {'sender': 'test()', 'target': 'Short Wait', 'command': 'wait'}
- test(): {'sender': 'Short Wait', 'event': 'finished'}
- test(): {'sender': 'Long Wait', 'event': 'finished'}
- """
-
- CONF_SCHEMA = {'properties': {'seconds': {'type': 'number'}},
- 'required': ['seconds']}
- """Schema for Wait plugin configuration.
-
- Required configuration key:
-
- - 'seconds': number of seconds to wait.
- """
-
- async def wait(self, message: Message) -> None:
- """Wait configured time and send "finished" event."""
- async def wait_coroutine():
- await asyncio.sleep(self.conf['seconds'])
- await self.bus.send(Message(self.name, {'event': 'finished'}))
- # Done in separate task to not block queue awaiting this callback:
- asyncio.create_task(wait_coroutine())
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'wait'}})]
- sends = [MessageTemplate({'event': {'const': 'finished'}})]
- self.bus.register(self.name, 'Wait', sends, receives, self.wait)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for Wait plugin configuration.
-
Required configuration key:
-
-'seconds': number of seconds to wait.
-
-
-
-Methods
-
-
-async def wait (self, message:Â Message ) â>Â NoneType
-
-
-Wait configured time and send "finished" event.
-
-
-Expand source code
-
-async def wait(self, message: Message) -> None:
- """Wait configured time and send "finished" event."""
- async def wait_coroutine():
- await asyncio.sleep(self.conf['seconds'])
- await self.bus.send(Message(self.name, {'event': 'finished'}))
- # Done in separate task to not block queue awaiting this callback:
- asyncio.create_task(wait_coroutine())
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'wait'}})]
- sends = [MessageTemplate({'event': {'const': 'finished'}})]
- self.bus.register(self.name, 'Wait', sends, receives, self.wait)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-class GenericWait
-( bus: MessageBus , name: str, conf: Dict[str, Any])
-
-
-Wait for time defined in "wait" command.
-
The "wait" command has message keys "seconds" defining the seconds to
-wait and "id" defining a string to be sent back in the "finished" event
-after the wait:
-
>>> import controlpi
->>> asyncio.run(controlpi.test(
-... {"Test GenericWait": {"plugin": "GenericWait"}},
-... [{"target": "Test GenericWait", "command": "wait",
-... "seconds": 0.02, "id": "Long Wait"},
-... {"target": "Test GenericWait", "command": "wait",
-... "seconds": 0.01, "id": "Short Wait"}], 0.025))
-... # doctest: +NORMALIZE_WHITESPACE
-test(): {'sender': '', 'event': 'registered',
- 'client': 'Test GenericWait', 'plugin': 'GenericWait',
- 'sends': [{'event': {'const': 'finished'},
- 'id': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Test GenericWait'},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}}]}
-test(): {'sender': 'test()', 'target': 'Test GenericWait',
- 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
-test(): {'sender': 'test()', 'target': 'Test GenericWait',
- 'command': 'wait', 'seconds': 0.01, 'id': 'Short Wait'}
-test(): {'sender': 'Test GenericWait', 'event': 'finished',
- 'id': 'Short Wait'}
-test(): {'sender': 'Test GenericWait', 'event': 'finished',
- 'id': 'Long Wait'}
-
-
-
-Expand source code
-
-class GenericWait(BasePlugin):
- """Wait for time defined in "wait" command.
-
- The "wait" command has message keys "seconds" defining the seconds to
- wait and "id" defining a string to be sent back in the "finished" event
- after the wait:
- >>> import controlpi
- >>> asyncio.run(controlpi.test(
- ... {"Test GenericWait": {"plugin": "GenericWait"}},
- ... [{"target": "Test GenericWait", "command": "wait",
- ... "seconds": 0.02, "id": "Long Wait"},
- ... {"target": "Test GenericWait", "command": "wait",
- ... "seconds": 0.01, "id": "Short Wait"}], 0.025))
- ... # doctest: +NORMALIZE_WHITESPACE
- test(): {'sender': '', 'event': 'registered',
- 'client': 'Test GenericWait', 'plugin': 'GenericWait',
- 'sends': [{'event': {'const': 'finished'},
- 'id': {'type': 'string'}}],
- 'receives': [{'target': {'const': 'Test GenericWait'},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}}]}
- test(): {'sender': 'test()', 'target': 'Test GenericWait',
- 'command': 'wait', 'seconds': 0.02, 'id': 'Long Wait'}
- test(): {'sender': 'test()', 'target': 'Test GenericWait',
- 'command': 'wait', 'seconds': 0.01, 'id': 'Short Wait'}
- test(): {'sender': 'Test GenericWait', 'event': 'finished',
- 'id': 'Short Wait'}
- test(): {'sender': 'Test GenericWait', 'event': 'finished',
- 'id': 'Long Wait'}
- """
-
- CONF_SCHEMA = True
- """Schema for GenericWait plugin configuration.
-
- There are no required or optional configuration keys.
- """
-
- async def wait(self, message: Message) -> None:
- """Wait given time and send "finished" event with given "id"."""
- async def wait_coroutine():
- assert isinstance(message['seconds'], float)
- await asyncio.sleep(message['seconds'])
- await self.bus.send(Message(self.name, {'event': 'finished',
- 'id': message['id']}))
- # Done in separate task to not block queue awaiting this callback:
- asyncio.create_task(wait_coroutine())
-
- def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}})]
- sends = [MessageTemplate({'event': {'const': 'finished'},
- 'id': {'type': 'string'}})]
- self.bus.register(self.name, 'GenericWait', sends, receives, self.wait)
-
- async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-Ancestors
-
-Class variables
-
-var CONF_SCHEMA
-
-Schema for GenericWait plugin configuration.
-
There are no required or optional configuration keys.
-
-
-Methods
-
-
-async def wait (self, message:Â Message ) â>Â NoneType
-
-
-Wait given time and send "finished" event with given "id".
-
-
-Expand source code
-
-async def wait(self, message: Message) -> None:
- """Wait given time and send "finished" event with given "id"."""
- async def wait_coroutine():
- assert isinstance(message['seconds'], float)
- await asyncio.sleep(message['seconds'])
- await self.bus.send(Message(self.name, {'event': 'finished',
- 'id': message['id']}))
- # Done in separate task to not block queue awaiting this callback:
- asyncio.create_task(wait_coroutine())
-
-
-
-def process_conf (self) â>Â NoneType
-
-
-Register plugin as bus client.
-
-
-Expand source code
-
-def process_conf(self) -> None:
- """Register plugin as bus client."""
- receives = [MessageTemplate({'target': {'const': self.name},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}})]
- sends = [MessageTemplate({'event': {'const': 'finished'},
- 'id': {'type': 'string'}})]
- self.bus.register(self.name, 'GenericWait', sends, receives, self.wait)
-
-
-
-async def run (self) â>Â NoneType
-
-
-
-
-
-Expand source code
-
-async def run(self) -> None:
- """Run no code proactively."""
- pass
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/index.md b/doc/index.md
index d67a64e..faa0e12 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -25,9 +25,9 @@ Die ControlPi-Infrastruktur hat zwei Haupt-Bestandteile:
Dictionaries bzw. Mappings.)
Die generierte Dokumentation des API der grundlegenden Infrastruktur ist
-unter [controlpi/](graphit/controlpi/controlpi/) zu finden.
+unter [api/controlpi/](graphit/controlpi/api/controlpi/) zu finden.
Die Kollektion an grundlegenden Plugins ist unter
-[controlpi_plugins/](graphit/controlpi/controlpi_plugins/) dokumentiert.
+[api/controlpi_plugins/](graphit/controlpi/api/controlpi_plugins/) dokumentiert.
Um Nachrichten zu senden und/oder zu empfangen muss ein Klient am Bus unter
einem Namen registriert werden.
@@ -481,24 +481,10 @@ editierbar installiert werden:
```
## Code-Stil, Typ-Checks und Tests
-Die Formatierung des Codes und das Vorhandensein und der Stil der
-Kommentare werden durch
-[`pycodestyle`](https://pycodestyle.pycqa.org/en/latest/) und
-[`pydocstyle`](http://www.pydocstyle.org/en/stable/) überprüft.
-Die an Funktionen und Variablen annotierten Typ-Informationen werden durch
-[`mypy`](http://mypy-lang.org/) gecheckt.
-Alle drei Tools können rekursiv ein gesamtes Python-Paket überprüfen:
-```sh
-(venv)$ pycodestyle /controlpi
-(venv)$ pydocstyle /controlpi
-(venv)$ mypy /controlpi
-```
-
-Sie sind als Extra-Requirements in der Datei `setup.py` definiert, sodass
-sie mit einem einzigen `pip`-Aufruf installiert werden können:
-```sh
-(venv)$ pip install --editable [dev]
-```
+Die Formatierung des Codes und das Vorhandensein und der Stil der Kommentare
+werden durch [`ruff`](https://docs.astral.sh/ruff/) und die an Funktionen und
+Variablen annotierten Typ-Informationen werden durch
+[`ty`](https://docs.astral.sh/ty/) überprüft.
Der Code wird durch in die Dokumentation eingebettete âdoctestsâ getestet.
Diese können für jede Code-Datei einzeln mit dem in der
@@ -517,14 +503,10 @@ des Codes mit Tests erhält man mit der zusätzlichen Option `-v`:
(venv)$ python -m doctest -v
```
-AuÃerdem wird durch die `[dev]`-Extras in `setup.py` auch das Tool
-[`pdoc`](https://pdoc3.github.io/pdoc/) zur automatischen Generierung von
-API-Dokumentation in HTML installiert:
+AuÃerdem wird das Tool
+[`pdoc`](https://pdoc.dev/docs/pdoc.html) zur automatischen Generierung von
+API-Dokumentation in HTML benutzt:
```sh
-(venv)$ pdoc --html --config sort_identifiers=False --force \
- --output-dir doc/ controlpi/ controlpi_plugins/
+(venv)$ pdoc -o doc/api/ controlpi controlpi_plugins
```
-Mit diesem wurden auch die oben verlinkten API-Dokumentationen für
-[`controlpi`](graphit/controlpi/controlpi/) und
-[`controlpi_plugins`](graphit/controlpi/controlpi_plugins/)
-generiert.
+Mit diesem wurden auch die oben verlinkten API-Dokumentationen generiert.
diff --git a/setup.py b/setup.py
index 275a14c..47bed5d 100644
--- a/setup.py
+++ b/setup.py
@@ -19,14 +19,6 @@ setuptools.setup(
"fastjsonschema",
"pyinotify",
],
- extras_require={
- "dev": [
- "pycodestyle",
- "pydocstyle",
- "mypy",
- "pdoc3",
- ]
- },
classifiers=[
"Programming Language :: Python",
"License :: OSI Approved :: MIT License",