Source code for perfsim.observers.observable
# Copyright (C) 2020 Michel Gokan Khan
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU 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.
#
# This file is a part of the PerfSim project, which is now open source and available under the GPLv2.
# Written by Michel Gokan Khan, February 2020
from abc import ABC, abstractmethod
from typing import Dict, Set
from perfsim import EventObserver
[docs]
class Observable(ABC):
"""
This class is an abstract class that represents an observable.
"""
#: A dictionary of observers, indexed by event type.
observers: Dict[str, Set[EventObserver]]
notify_observers_on_event: bool
registered_events: Set[str]
[docs]
def attach_observer(self, observer: EventObserver):
"""
Attach an observer to the observable.
:param observer: The observer to attach.
:return: None
"""
for event_name in observer.events:
if event_name not in self.observers.keys():
if event_name not in self.registered_events:
raise Exception(f"Event {event_name} is not registered. "
f"Please first register the event using register_event method.")
self.observers[event_name] = set()
self.observers[event_name].add(observer)
[docs]
def notify_all_observers(self, **kwargs):
"""
Notify all observers.
:param kwargs: The arguments to pass to the observers.
:return:
"""
for event_name, observers in self.observers.items():
for observer in observers:
observer.observe(**kwargs)
[docs]
def notify_observers(self, event_name: str, **kwargs):
"""
Notify the observers of an event.
:param event_name: The name of the event.
:param kwargs: The arguments to pass to the observers.
:return: None
"""
if self.notify_observers_on_event and event_name in self.observers:
for observer in self.observers[event_name]:
observer.observe(event_name=event_name, **kwargs)
[docs]
def detach_observer(self, observer: EventObserver):
"""
Detach an observer from the observable.
:param observer: The observer to detach.
:return: None
"""
for event_name in observer.events:
self.observers[event_name].discard(observer)
[docs]
def register_event(self, event_name: str):
"""
Register an event.
:param event_name:
:return:
"""
setattr(self, event_name, event_name)
self.registered_events.add(event_name)
def __init__(self):
self.observers = {}
self.notify_observers_on_event = True
self.registered_events = set()
self.register_events()
[docs]
@abstractmethod
def register_events(self):
"""
This is for performance optimization purposes. Instead of generating strings for each event, we can register the
event_names as attributes, then we send the event_name as reference, instead of copying the string every time.
This (slightly) improves performance, specially because we are calling the notify_observers method several times
for each request during the simulation.
:return:
"""
pass