124 lines
4.5 KiB
Python

# eventpy library
# Copyright (C) 2020 Wang Qi (wqking)
# Github: https://github.com/wqking/eventpy
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import eventpy.policy
import eventpy.internal.linkedlist as linkedlist
import eventpy.internal.lockguard as lockguard
import inspect
class CallbackList :
class NodeData :
def __init__(self, callback, counter) :
self._callback = callback
self._counter = counter
def __init__(self, policy = eventpy.policy.defaultPolicy) :
self._policy = policy.clone()
self._lock = self._policy.lockClass()
self._list = linkedlist.LinkedList(self._lock)
self._currentCounter = 0
def empty(self) :
return self._list.empty()
def append(self, callback) :
node = linkedlist.LinkedListNode(CallbackList.NodeData(callback, self._getNextCounter()))
self._list.append(node)
return node
def prepend(self, callback) :
node = linkedlist.LinkedListNode(CallbackList.NodeData(callback, self._getNextCounter()))
self._list.prepend(node)
return node
def insert(self, callback, before) :
node = linkedlist.LinkedListNode(CallbackList.NodeData(callback, self._getNextCounter()))
self._list.insert(node, before)
return node
def remove(self, node) :
if node is not None :
def postProcess(n) :
n.getData()._counter = 0
self._list.remove(node, postProcess)
def forEach(self, func) :
with lockguard.LockGuard(self._lock) :
node = self._list.getHead()
counter = self._currentCounter
invoke = _selectForEachInvoke(func)
while node is not None :
nodeCounter = node.getData()._counter
if nodeCounter != 0 and counter >= nodeCounter :
invoke(node)
with lockguard.LockGuard(self._lock) :
node = node._next
def forEachIf(self, func) :
with lockguard.LockGuard(self._lock) :
node = self._list.getHead()
counter = self._currentCounter
invoke = _selectForEachInvoke(func)
while node is not None :
nodeCounter = node.getData()._counter
if nodeCounter != 0 and counter >= nodeCounter :
if not invoke(node) :
return False
with lockguard.LockGuard(self._lock) :
node = node._next
return True
def __call__(self, *args, **kwargs) :
with lockguard.LockGuard(self._lock) :
node = self._list.getHead()
counter = self._currentCounter
while node is not None :
nodeCounter = node.getData()._counter
if nodeCounter != 0 and counter >= nodeCounter :
node.getData()._callback(*args, **kwargs)
if not self._policy.canContinueInvoking(*args, **kwargs) :
break
with lockguard.LockGuard(self._lock) :
node = node._next
def _getNextCounter(self) :
self._currentCounter += 1
result = self._currentCounter
if result <= 0 : # overflow, let's reset all nodes' counters.
with lockguard.LockGuard(self._lock) :
node = self._list.getHead()
while node is not None :
node.getData()._counter = 1
node = node.getNext()
if self._currentCounter < 0 :
self._currentCounter = 0
self._currentCounter += 1
result = self._currentCounter
return result
def _selectForEachInvoke(func) :
def _forEachInvoke1(node, func = func) :
return func(node.getData()._callback)
def _forEachInvoke2(node, func = func) :
return func(node, node.getData()._callback)
sig = inspect.signature(func)
if len(sig.parameters) >= 2 :
return _forEachInvoke2
else :
return _forEachInvoke1