93 lines
3.4 KiB
Python
93 lines
3.4 KiB
Python
"""
|
|
Copyright 2009, Linden Research, Inc.
|
|
See NOTICE.md for previous contributors
|
|
Copyright 2021, Salad Dais
|
|
All Rights Reserved.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 3 of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
"""
|
|
import asyncio
|
|
import logging
|
|
|
|
from hippolyzer.lib.base.helpers import create_logged_task
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class Event:
|
|
""" an object containing data which will be passed out to all subscribers """
|
|
|
|
def __init__(self, name=None):
|
|
self.subscribers = []
|
|
self.name = name
|
|
|
|
def subscribe(self, handler, *args, one_shot=False, predicate=None, **kwargs):
|
|
""" establish the subscribers (handlers) to this event """
|
|
handler_tup = (handler, args, kwargs, one_shot, predicate)
|
|
assert handler_tup not in self.subscribers
|
|
self.subscribers.append(handler_tup)
|
|
|
|
return self
|
|
|
|
@staticmethod
|
|
def _handler_key(handler):
|
|
return handler[:3]
|
|
|
|
def unsubscribe(self, handler, *args, **kwargs):
|
|
""" remove the subscriber (handler) to this event """
|
|
did_remove = False
|
|
for registered in reversed(self.subscribers):
|
|
if self._handler_key(registered) == (handler, args, kwargs):
|
|
self.subscribers.remove(registered)
|
|
did_remove = True
|
|
if not did_remove:
|
|
raise ValueError(f"Handler {handler!r} is not subscribed to this event.")
|
|
return self
|
|
|
|
def _create_async_wrapper(self, handler, args, inner_args, kwargs):
|
|
# Note that unsubscription may be delayed due to asyncio scheduling :)
|
|
async def _run_handler_wrapper():
|
|
unsubscribe = await handler(args, *inner_args, **kwargs)
|
|
if unsubscribe:
|
|
_ = self.unsubscribe(handler, *inner_args, **kwargs)
|
|
return _run_handler_wrapper
|
|
|
|
def notify(self, args):
|
|
for subscriber in self.subscribers[:]:
|
|
handler, inner_args, kwargs, one_shot, predicate = subscriber
|
|
if predicate and not predicate(args):
|
|
continue
|
|
if one_shot:
|
|
self.unsubscribe(handler, *inner_args, **kwargs)
|
|
if asyncio.iscoroutinefunction(handler):
|
|
create_logged_task(self._create_async_wrapper(handler, args, inner_args, kwargs)(), self.name, LOG)
|
|
else:
|
|
try:
|
|
if handler(args, *inner_args, **kwargs) and not one_shot:
|
|
self.unsubscribe(handler, *inner_args, **kwargs)
|
|
except:
|
|
# One handler failing shouldn't prevent notification of other handlers.
|
|
LOG.exception(f"Failed in handler for {self.name}")
|
|
|
|
def __len__(self):
|
|
return len(self.subscribers)
|
|
|
|
def clear_subscribers(self):
|
|
self.subscribers.clear()
|
|
|
|
__iadd__ = subscribe
|
|
__isub__ = unsubscribe
|
|
__call__ = notify
|