# -*- coding: utf-8 -*-
from collections import namedtuple, OrderedDict, UserList
from .serializable import Serializable
from .annotatable import Annotatable
from .eq import DeepEqual
from ..utils.common import trbk, lls
import logging
# create logger
logger = logging.getLogger(__name__)
# logger.debug('level %d' % (logger.getEffectiveLevel()))
[docs]class EventListener(Annotatable):
""" Generic interface for listeners that will listen to anything
"""
def __init__(self, *args, **kwds):
super().__init__(*args, **kwds) # EventListener
[docs] def targetChanged(self, *args, **kwargs):
""" Informs that an event has happened in a target of
any type.
Paremeters
----------
Returns
-------
"""
pass
# class xDatasetBaseListener(Annotatable):
# """ Generic interface for listeners that will listen to events
# happening on a target of a specific type.
# Java Warning:
# The listener must be a class field in order to make an object
# hard reference.
# """
# def __init__(self, *args, **kwds):
# """
# Parameters
# ----------
# Returns
# -------
# """
# super().__init__(*args, **kwds) # DatasetBaseListener
# def targetChanged(self, event):
# """ Informs that an event has happened in a target of the
# specified type.
# Paremeters
# ----------
# Returns
# -------
# """
# pass
[docs]class ListenerSet(Serializable, DeepEqual, UserList):
""" Mutable collection of Listeners of an EvenSender.
"""
[docs] def __init__(self, data=None, *args, **kwds):
"""
Parameters
----------
:data: default is `None` for a list.
Returns
-----
"""
if data is None:
data = []
super().__init__(data, *args, **kwds)
@property
def urns(self):
"""
Parameters
----------
Returns
-----
"""
return self.geturns()
@urns.setter
def urns(self, urns):
"""
Parameters
----------
Returns
-----
"""
self.seturns(urns)
[docs] def seturns(self, urns):
""" Replaces the current urn with specified argument.
Parameters
----------
Returns
-----
"""
for urn in urns:
try:
l = ProductRef(urn).product
except ValueError as e:
logger.warn(str(e))
continue
self.addListener(l)
[docs] def geturns(self, remove=None):
""" Returns the current urns.
Parameters
----------
Returns
"""
ret = [ProductRef(
x).urn for x in self.data if remove is None or x != remove]
return ret
[docs] def equals(self, obj, verbose=False):
""" compares with another one.
Parameters
----------
Returns
"""
return True
def __getstate__(self):
""" Can be encoded with serializableEncoder
Parameters
----------
Returns
"""
return OrderedDict()
def __repr__(self, **kwds):
return self.toString(level=2)
[docs] def toString(self, level=0, alist=False, **kwds):
"""
Parameters
----------
Returns
-------
LIST[TUPLE(OBJ)] or STRXS
A list of member-repre tuples or a string of all depending on `alist`.
"""
if level == 0:
if alist:
l = [(x.__class__.__name__, id(x),
lls((x.description if hasattr(x, 'description') else 'UNKNOWN'), 20))
for x in self.data]
else:
l = ['%s(%d, %s)' % (x.__class__.__name__, id(x),
lls((x.description if hasattr(x, 'description') else 'UNKNOWN'), 20))
for x in self.data]
else:
if alist:
l = [(x.__class__.__name__, id(x),
lls((x.description if hasattr(x, 'description') else 'UNKNOWN'), 8))
for x in self.data]
else:
l = ['%s(%d, %s)' % (x.__class__.__name__, id(x),
lls((x.description if hasattr(x, 'description') else 'UNKNOWN'), 8))
for x in self.data]
if alist:
return l
else:
return self.__class__.__name__ + '(' + ', '.join(l) + ')'
string = toString
txt = toString
[docs]class EventSender():
""" adapted from Peter Thatcher's
https://stackoverflow.com/questions/1092531/event-system-in-python/1096614#1096614
"""
[docs] def __init__(self, **kwds):
"""
Parameters
----------
Returns
"""
self._listeners = ListenerSet()
super().__init__(**kwds) # EventSender
@property
def listeners(self):
"""
Parameters
----------
Returns
"""
return self.getListeners()
@listeners.setter
def listeners(self, listeners):
"""
Parameters
----------
Returns
"""
self.setListeners(listeners)
[docs] def setListeners(self, listeners):
""" Replaces the current Listeners with specified argument.
Paremeters
----------
Returns
-------
"""
self._listeners = ListenerSet()
if listeners:
for listener in listeners:
self.addListener(listener)
[docs] def getListeners(self):
""" Returns the current Listeners.
Paremeters
----------
Returns
-------
"""
return self._listeners
[docs] def addListener(self, listener, cls=EventListener):
""" Adds a listener to this.
Paremeters
----------
Returns
-------
"""
l = listener
if issubclass(l.__class__, cls):
if l not in self._listeners:
self._listeners.append(l)
else:
raise TypeError(
'Listener is not subclass of ' + str(cls) + ' .')
return self
[docs] def removeListener(self, listener):
""" Removes a listener from this.
Paremeters
----------
Returns
-------
"""
try:
self._listeners.remove(listener)
except:
raise ValueError(
"Listener has no listening registerd. Cannot remove.")
return self
[docs] def fire(self, *args, **kwargs):
"""
Paremeters
----------
Returns
-------
"""
n = 0
try:
for listener in self._listeners:
listener.targetChanged(*args, **kwargs)
n += 1
except Exception as e:
logger.error('listener ' + str(n) +
' got exception: ' + str(e) + ' ' + trbk(e))
raise
[docs] def getListenerCount(self):
"""
Paremeters
----------
Returns
-------
"""
return len(self._listeners)
__call__ = fire
# __len__ = getHandlerCount
[docs]class DatasetEventSender(EventSender):
[docs] def __init__(self, **kwds): # DatasetEventSender
"""
Paremeters
----------
Returns
-------
"""
super().__init__(**kwds) # DatasetEventSender
EventTypes = [
# A column has been added to the target TableDataset.
'COLUMN_ADDED',
# A column has been changed in the target TableDataset.
'COLUMN_CHANGED',
# A column has been removed from the target TableDataset.
'COLUMN_REMOVED',
# The targets data has changed.
'DATA_CHANGED',
# A dataset has been added to the target composite.
'DATASET_ADDED',
# A dataset has been changed in the target composite.
'DATASET_CHANGED',
# A dataset has been removed from the target composite.
'DATASET_REMOVED',
# The targets has changed.
'DESCRIPTION_CHANGED',
# The targets MetaData has been changed.
'METADATA_CHANGED',
# A parameter has been added to the target meta data.
'PARAMETER_ADDED',
# A parameter has been changed in the target meta data.
'PARAMETER_CHANGED',
# A parameter has been removed from the target meta data.
'PARAMETER_REMOVED',
# A row has been added to the target TableDataset.
'ROW_ADDED',
# A row has been removed from the target TableDataset.
'ROW_REMOVED',
# The targets unit has changed.
'UNIT_CHANGED',
# Some value in the target object has changed.
'VALUE_CHANGED',
# Some attributes in the target object has changed.
'UNKNOWN_ATTRIBUTE_CHANGED',
]
# EventTd['VALUE_CHANGED']='VALUE_CHANGED'
EventTd = dict([(e, e) for e in EventTypes])
# EventType.VALUE_CHANGED = 'VALUE_CHANGED'
EventType = namedtuple('EventType', EventTypes)(**EventTd)
# e.g. eventTypeof['CHANGED']['UNIT'] gives 'UNIT_CHANGED'
EventTypeOf = {}
for evt in EventTypes:
t = evt.rsplit('_', 1)
if t[1] in EventTypeOf:
EventTypeOf[t[1]][t[0]] = evt
else:
EventTypeOf[t[1]] = {}
EventTypeOf[t[1]][t[0]] = evt
[docs]class DatasetEvent(Serializable):
"""
"""
[docs] def __init__(self, source, target, typ_, change, cause, rootCause, **kwds):
"""
Paremeters
----------
Returns
-------
"""
# The object on which the Event initially occurred.
self.source = source
# the target of the event, which is the same object returned
# by getSource, but strongly typed.
if isinstance(target, source.__class__):
self.target = target
else:
raise TypeError(str(target) + ' is not of type ' +
str(source.__class__))
# the type of the event.
self.type = typ_
# Gives more information about the change that caused the event.
self.change = change
# The underlying event that provoked this event,
# or null if there is no finer cause.
self.cause = cause
# The first event in the chain that provoked this event,
# or null if this event is its own root.
self.rootCause = rootCause
super().__init__(**kwds) # DatasetEvent
[docs] def toString(self, level=0, **kwds):
return self.__repr__()
string = toString
txt = toString
def __getstate__(self):
""" Can be encoded with serializableEncoder
Paremeters
----------
Returns
-------
"""
s = OrderedDict(source=self.source,
target=self.target,
typ_=self.type,
change=self.change,
cause=self.cause,
rootCause=self.rootCause)
return s
[docs]class ParameterListener(EventListener):
""" Listener for events occuring in a Parameter.
Available types::
* DESCRIPTION_CHANGED
* UNIT_CHANGED
* VALUE_CHANGED
* UNKOWN_ATTRIBUTE_CHANGED
Cause is always null.
Warning: The listener handler must be a class attribute in order to
create an object hard reference. See DatasetBaseListener.
"""
pass
[docs] def __init__(self, **kwds):
"""
Parameters
----------
Returns
-------
"""
super().__init__(**kwds)
[docs]class DatasetListener(EventListener):
""" Listener for events occuring in MetaData.
Available types::
* DESCRIPTION_CHANGED, METADATA_CHANGED (all datasets)
* DATA_CHANGED, UNIT_CHANGED (ArrayDataset)
* COLUMN_ADDED, COLUMN_REMOVED, COLUMN_CHANGED, ROW_ADDED, VALUE_CHANGED (TableDataset)
* DATASET_ADDED, DATASET_REMOVED, DATASET_CHANGED (CompositeDataset)
Possible causes::
* not null (METADATA_CHANGED, COLUMN_CHANGED, DATASET_CHANGED)
* null (rest)
Warning: The listener handler must be a class attribute in order to
create an object hard reference. See DatasetBaseListener.
"""
[docs] def __init__(self, **kwds):
"""
Parameters
----------
Returns
-------
"""
super().__init__(**kwds) # DatasetListener
[docs]class ColumnListener(EventListener):
""" Listener for events occuring in a Column.
Available types::
* DESCRIPTION_CHANGED
* UNIT_CHANGED
* DATA_CHANGED
Cause is always null.
"""
[docs] def __init__(self, **kwds):
"""
Parameters
----------
Returns
-------
"""
super().__init__(**kwds) # ColumnListener
[docs]class ProductListener(EventListener):
""" Listener for events occuring in Product.
Available types::
* METADATA_CHANGED
* DATASET_ADDED
* DATASET_REMOVED
* DATASET_CHANGED
Possible causes::
* not null (METADATA_CHANGED, DATASET_CHANGED)
* null (METADATA_CHANGED, DATASET_REMOVED, DATASET_CHANGED)
Warning: The listener handler must be a class attribute in order to
create an object hard reference. See DatasetBaseListener.
"""
[docs] def __init__(self, **kwds):
"""
Parameters
----------
Returns
-------
"""
super().__init__(**kwds) # ProductListener