Search code examples
python-3.xooploggingsoftware-design

How to eloquently keep a track of all warnings/messages during an api call and return the same along with the output in python?


Method 1

warnings = list()
messages = list()

def calculate(a, b, method):
    if isinstance(a, float):
        warnings.append(f"a:{a} is float, converting it to int!")
        a = int(a)
    ...
    result = a + b
    messages.append(f"result is {result}")
    return result, {"warnings": warnings, "messages": messasges}

In the example above, I have a simple function calculate() and I am keeping a track of all the warnings/messages during this run and then returning the same with the actual result because let's say I want to display these warnings/messages to the end user on UI along with the actual result.

Another better way to do the same would be:

Method 2

class Alerts():
    def __init__():
        self.warnings = list()
        self.messages = list()

def calculate(a, b, method):
    alrt = Alerts()
    if isinstance(a, float):
        alrt.warnings.append(f"a:{a} is float, converting it to int!")
        a = int(a)
    ...
    result = a + b
    alrt.messages.append(f"result is {result}")
    return result, alrt

In Method 2, it's more convenient to keep track of all messages/warnings by defining them in a single object and this will also allow to write some custom methods in Alert() class for formatting messages/warnings, et cetera.

Potential Issue

If there are several method calls involved in an api call, then I would have to create/maintain this Alert() object everywhere and/or toss it around in my methods to keep a track of ALL warnings/messages in one single object.

Is one of the above methods a good way to achieve this? Can it be made more eloquent?

Thanks!


Solution

  • After some thinking, below is the most eloquent way I could come up with. calculate() is my api function, sum() is one of the internal method calls in the api run.

    # for handling results of any api, including errors/warnings/messages
    class ResultHandler():
        def __init__(self):
            self.warnings = list()
            self.messages = list()
            self.errors = list()
            self.result = dict()
    
        def append_traceback(self):
            pass
    
    # to wrap the output of an api call in ResultHandler() object
    def wrap_output(func):
        def wrapper(*args, **kwargs):
            rh = ResultHandler()
            rh.messages.append("1: i am wrap_output!")
            try:
                kwargs["rh"] = rh
                rh.result = func(*args, **kwargs)
            except:
                rh.append_traceback()
            rh.messages.append("2: i am wrap_output!")
            return rh
        return wrapper
    
    # an internal method call
    def sum(rh):
        print("sum")
        rh.messages.append("i am sum!")
        return 1
    
    # api function
    @wrap_output
    def calculate(*args, **kwargs):
        rh = kwargs["rh"]
    
        rh.messages.append("1: i am calculate!")
    
        out = sum(*args, **kwargs)
    
        rh.messages.append("2: i am calculate!")
        return out
    
    # calling api
    o = calculate()
    print(o.messages)