2018-11-30 23:27:11 +01:00
|
|
|
import asyncio
|
2018-11-18 23:09:17 +01:00
|
|
|
import inspect
|
2018-11-01 19:50:04 +01:00
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
|
|
class EventBus(object):
|
|
|
|
class Node(object):
|
|
|
|
__slots__ = '_children', '_content'
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self._children = {}
|
|
|
|
self._content = None
|
|
|
|
|
2018-11-29 21:59:08 +01:00
|
|
|
class Content(object):
|
|
|
|
def __init__(self, parent, topic, method, once):
|
|
|
|
self.parent = parent
|
|
|
|
self.method = method
|
|
|
|
self.name = method.__name__
|
|
|
|
self.once = once
|
|
|
|
self.topic = topic
|
|
|
|
|
|
|
|
def register(self, topic, method, once=False):
|
2018-11-30 23:27:11 +01:00
|
|
|
|
2018-11-29 21:59:08 +01:00
|
|
|
if method in self.registry:
|
|
|
|
raise RuntimeError("Method %s already registerd. Please unregister first!" % method.__name__)
|
|
|
|
self.logger.info("Topic %s", topic)
|
2018-11-01 19:50:04 +01:00
|
|
|
node = self._root
|
2018-11-29 21:59:08 +01:00
|
|
|
for sym in topic.split('/'):
|
2018-11-01 19:50:04 +01:00
|
|
|
node = node._children.setdefault(sym, self.Node())
|
|
|
|
|
|
|
|
if not isinstance(node._content, list):
|
|
|
|
node._content = []
|
2018-11-29 21:59:08 +01:00
|
|
|
|
|
|
|
c = self.Content(node, topic, method, once)
|
|
|
|
node._content.append(c)
|
|
|
|
self.registry[method] = c
|
|
|
|
|
2018-11-01 19:50:04 +01:00
|
|
|
def get_callbacks(self, key):
|
|
|
|
try:
|
|
|
|
node = self._root
|
|
|
|
for sym in key.split('/'):
|
|
|
|
node = node._children[sym]
|
|
|
|
if node._content is None:
|
|
|
|
raise KeyError(key)
|
|
|
|
return node._content
|
|
|
|
except KeyError:
|
|
|
|
raise KeyError(key)
|
|
|
|
|
2018-11-29 21:59:08 +01:00
|
|
|
def unregister(self, method):
|
|
|
|
self.logger.info("Unregister %s", method.__name__)
|
|
|
|
if method in self.registry:
|
|
|
|
content = self.registry[method]
|
|
|
|
clean_idx = None
|
|
|
|
for idx, content_obj in enumerate(content.parent._content):
|
|
|
|
if method == content_obj.method:
|
|
|
|
clean_idx = idx
|
|
|
|
break
|
|
|
|
if clean_idx is not None:
|
|
|
|
del content.parent._content[clean_idx]
|
|
|
|
|
2018-11-30 23:27:11 +01:00
|
|
|
def __init__(self, loop):
|
2018-11-01 19:50:04 +01:00
|
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
self._root = self.Node()
|
2018-11-29 21:59:08 +01:00
|
|
|
self.registry = {}
|
2018-11-01 19:50:04 +01:00
|
|
|
self.docs = {}
|
2018-11-30 23:27:11 +01:00
|
|
|
if loop is not None:
|
|
|
|
self.loop = loop
|
|
|
|
else:
|
|
|
|
self.loop = asyncio.get_event_loop()
|
|
|
|
|
|
|
|
print(self.loop)
|
2018-11-01 19:50:04 +01:00
|
|
|
|
2018-11-16 20:35:59 +01:00
|
|
|
def fire(self, topic: str, **kwargs) -> None:
|
|
|
|
self.logger.info("EMIT EVENT %s", topic)
|
2018-11-29 21:59:08 +01:00
|
|
|
|
2018-11-30 23:27:11 +01:00
|
|
|
trx = dict(i=0)
|
|
|
|
for e in self.iter_match(topic):
|
|
|
|
content_array = e
|
|
|
|
keep_idx = []
|
2018-11-29 21:59:08 +01:00
|
|
|
for idx, content_obj in enumerate(content_array):
|
|
|
|
|
|
|
|
if inspect.iscoroutinefunction(content_obj.method):
|
2018-11-30 23:27:11 +01:00
|
|
|
if hasattr(content_obj.method, "future"):
|
|
|
|
|
|
|
|
self.loop.create_task(content_obj.method(**kwargs, future=content_obj.method.future, topic=topic))
|
|
|
|
else:
|
|
|
|
self.loop.create_task(content_obj.method(**kwargs, topic = topic))
|
2018-11-18 23:09:17 +01:00
|
|
|
else:
|
2018-11-30 23:27:11 +01:00
|
|
|
if hasattr(content_obj.method, "future"):
|
|
|
|
content_obj.method(**kwargs, future=content_obj.method.future, topic=topic)
|
|
|
|
else:
|
|
|
|
content_obj.method(**kwargs, topic = topic)
|
|
|
|
|
|
|
|
#if inspect.iscoroutinefunction(content_obj.method):
|
|
|
|
# self.loop.create_task(content_obj.method(**kwargs, trx=trx, topic=topic))
|
|
|
|
#else:
|
|
|
|
# content_obj.method(**kwargs, trx=trx, topic=topic)
|
|
|
|
if content_obj.once is False:
|
|
|
|
keep_idx.append(idx)
|
2018-11-29 21:59:08 +01:00
|
|
|
|
2018-11-30 23:27:11 +01:00
|
|
|
# FILTER only elements with are required
|
|
|
|
if len(keep_idx) < len(e):
|
|
|
|
e[0].parent._content = [e[0].parent._content[i] for i in keep_idx]
|
|
|
|
|
|
|
|
print("DONE", trx)
|
|
|
|
|
|
|
|
def dump(self):
|
|
|
|
def rec(node, i=0):
|
|
|
|
result = []
|
|
|
|
if node._content is not None:
|
|
|
|
for c in node._content:
|
|
|
|
result.append(dict(topic=c.topic, method=c.method.__name__, path=c.method.__module__, once=c.once))
|
2018-11-29 21:59:08 +01:00
|
|
|
|
2018-11-30 23:27:11 +01:00
|
|
|
if node._children is not None:
|
|
|
|
for c in node._children:
|
|
|
|
result = result + rec(node._children[c], i + 1)
|
|
|
|
return result
|
2018-11-29 21:59:08 +01:00
|
|
|
|
2018-11-30 23:27:11 +01:00
|
|
|
result = rec(self._root)
|
2018-11-29 21:59:08 +01:00
|
|
|
|
2018-11-30 23:27:11 +01:00
|
|
|
return result
|
2018-11-01 19:50:04 +01:00
|
|
|
|
|
|
|
def iter_match(self, topic):
|
|
|
|
|
|
|
|
lst = topic.split('/')
|
|
|
|
normal = not topic.startswith('$')
|
|
|
|
|
|
|
|
def rec(node, i=0):
|
|
|
|
if i == len(lst):
|
|
|
|
if node._content is not None:
|
|
|
|
yield node._content
|
|
|
|
else:
|
|
|
|
part = lst[i]
|
|
|
|
if part in node._children:
|
|
|
|
for content in rec(node._children[part], i + 1):
|
|
|
|
yield content
|
|
|
|
if '+' in node._children and (normal or i > 0):
|
|
|
|
for content in rec(node._children['+'], i + 1):
|
|
|
|
yield content
|
|
|
|
if '#' in node._children and (normal or i > 0):
|
|
|
|
content = node._children['#']._content
|
|
|
|
if content is not None:
|
|
|
|
yield content
|
|
|
|
|
|
|
|
return rec(self._root)
|