Callback#

Callback is a philosophy that is commonly used in LEADS. Some come in the form of callback objects, while others come in the form of single functions. This article only discusses the former, which are more common. If you encounter the latter, they are usually explained in the specific usage.

Callback Chain#

Registration of a callback is generally not a replacement. A newly registered callback is usually added as a new node to the callback chain. This callback chain is similar to a linked list.

LEADS implements this fundamental structure as CallbackChain. It provides the capability to be bound to another CallbackChain. It should be used as a template only. The following example defines a specific callback class and a use case.

from leads import CallbackChain


class MyCallback(CallbackChain):
    def on_my_callback_is_called(self, name: str) -> None:
        ...


class MyBusiness(object):
    def __init__(self) -> None:
        self._callback: MyCallback | None = None

    def set_callback(self, callback: MyCallback) -> None:
        callback.bind_chain(self._callback)
        self._callback = callback

    def do_something(self, name: str) -> None:
        if self._callback:
            self._callback.on_my_callback_is_called(name)

Now, the user can pass its callback object to the business.

from typing import override
from leads import CallbackChain, L


class MyCallback(CallbackChain):
    def on_my_callback_is_called(self, name: str) -> None:
        ...


class MyBusiness(object):
    def __init__(self) -> None:
        self._callback: MyCallback | None = None

    def set_callback(self, callback: MyCallback) -> None:
        callback.bind_chain(self._callback)
        self._callback = callback

    def do_something(self, name: str) -> None:
        if self._callback:
            self._callback.on_my_callback_is_called(name)


class UserCallbackA(MyCallback):
    @override
    def on_my_callback_is_called(self, name: str) -> None:
        self.super(name=name)
        L.info(f"My callback is called with an argument name={name}")


if __name__ == "__main__":
    my_business = MyBusiness()
    my_business.set_callback(UserCallbackA())
    my_business.do_something("test_case_1")

Important

It is advised to always call super() unless you want to override all previously declared methods.

Now suppose another user wants to register another callback object from somewhere else.

from typing import override
from leads import CallbackChain, L


class MyCallback(CallbackChain):
    def on_my_callback_is_called(self, name: str) -> None:
        ...


class MyBusiness(object):
    def __init__(self) -> None:
        self._callback: MyCallback | None = None

    def set_callback(self, callback: MyCallback) -> None:
        callback.bind_chain(self._callback)
        self._callback = callback

    def do_something(self, name: str) -> None:
        if self._callback:
            self._callback.on_my_callback_is_called(name)


class UserCallbackA(MyCallback):
    @override
    def on_my_callback_is_called(self, name: str) -> None:
        self.super(name=name)
        L.info(f"My callback is called with an argument name={name}")


class UserCallbackB(MyCallback):
    @override
    def on_my_callback_is_called(self, name: str) -> None:
        self.super(name=name)
        L.info(f"User B's callback is called with an argument name={name}")


if __name__ == "__main__":
    my_business = MyBusiness()
    my_business.set_callback(UserCallbackA())
    my_business.set_callback(UserCallbackB())
    my_business.do_something("test_case_2")

The output should be printing both “My callback is called with an argument name=test_case_2” and “User B’s callback is called with an argument name=test_case_2”.

Existing Callback Classes#

The two main callback classes are EventListener and Callback.