0

My requirement is to monitor a kafka topic for the messages & filter or search for a keyword - state from the arrived messages and print, Active Brands and Non active brands(Disables & Booted) separately. This has to be done within a timeout of 15 minutes or else break out of the script saying TimedOut.

Below is my piece of code and the sample message format from Kafka topic. I'm using confluent kafka.

Problems being, I'm unsure, where to add the timeout logic, should I do it in to_get_msg_from_kafka() or do it as part of status_check(). Secondly, ideally, All the messages until 15 minutes should be processed right or how should that be handled ?

Code Snippet:

START_TIME = datetime.now()
lap_list = ['Apple', 'Acer']
TIMEOUT = 15

#Function that polls kafka topic and retrieve messages..
def get_msg_from_kafka(args):
    pass

#Function that has to validate, whether keyword is present and also have to capture
#which laptops are in Active state and which aren't..
def check_state():
    success = True
    msg_status = get_msg_from_kafka(args) # This will return a JSON output as posted below.
    if bool(msg_status) == False:
        return False
    for val in msg_status['Brand']:
        if val['name'] in lap_list and val['state'] != "Active":
            success = False
        print(f"Status of {val['name']}: {val['Brand']}")
    return success

#Is it possible to check how long the validation took, i.e. to check the laptops are in active state.
def status_check():
    stop_time = START_TIME + timedelta(minutes=TIMEOUT)
    while True:
        success = check_state()
        if success:
            print("All the Laptops are in Active State..")
            break
        current_time = datetime.now()
        if current_time > stop_time:
            print("Exceeded the timeout ...")
            break

if __name__ == '__main__':
    status_check()

msg_status looks like below (Message format from Kafka)

msg_status = {  "case": "2nmi",
   "id": "6.00c",
   "key": "subN",
   "Brand":[
     {
         "state": "Active",
         "name": "Apple",
         "date": "2021-01-20T08:35:33.382532",
         "Loc": "GA",
      },
      {
         "state": "Disabled",
         "name": "HP",
         "date": "2018-01-09T08:25:90.382",
         "Loc": "TX",
      },
      
      {
         "state": "Active",
         "name": "Acer",
         "date": "2022-01-2T8:35:03.5432",
         "Loc": "IO"
      },
      {
         "state": "Booted",
         "name": "Toshiba",
         "date": "2023-09-29T9:5:03.3298",
         "Loc": "TX"
      }
   ],
   "DHL_ID":"a3288ec45c82"
}
voltas
  • 553
  • 7
  • 24

1 Answers1

0

I often use the following class and custom error to set timeouts. It works quite well and I find it Pythonic enough for my use cases.

import signal

class TimeoutError(Exception):
    """
    Custom error for Timeout class.
    """
    pass


class Timeout:
    """
    A timeout handler with context manager.
    Based on UNIX signals.
    """

    def __init__(self, seconds=1, error_message="Timeout"):
        self.seconds = seconds
        self.error_message = error_message

    def handle_timeout(self, signum, frame):
        raise TimeoutError(self.error_message)

    def __enter__(self):
        signal.signal(signal.SIGALRM, self.handle_timeout)
        signal.alarm(self.seconds)

    def __exit__(self, type, value, traceback):
        signal.alarm(0)

This is supposed to be used within a with block

with Timeout(seconds=15*60):
    try:
        do_stuff()
    except TimeoutError:
        do_something_else()
alec_djinn
  • 10,104
  • 8
  • 46
  • 71
  • You should probably reset the alarm handler (in \_\_exit__) to whatever was being used initially – DarkKnight Mar 24 '23 at 10:15
  • @DarkKnight Can you explain? It isn't clear to me what you mean. – alec_djinn Mar 24 '23 at 10:25
  • The signal function returns a reference to the handler that was previously assigned the relevant signal. You should reset to that value. You don't necessarily know the context in which your class is used so a handler may have been assigned elsewhere – DarkKnight Mar 24 '23 at 10:27
  • @DarkKnight somethis like this? on __init__ `self.original_sig_handler = signal.getsignal(signal.SIGALRM)` on __exit__ , `signal.signal(signal.SIGALRM, self.original_sig_handler)` (I am not sure if this should go before or after `signal.alarm(0)` – alec_djinn Mar 24 '23 at 10:46
  • It should go after the alarm cancellation – DarkKnight Mar 24 '23 at 12:20