Home > Net >  Python threading with timer - TypeError: 'NoneType' object is not callable
Python threading with timer - TypeError: 'NoneType' object is not callable

Time:09-17

I want to make a small function that does some fetching in the background for my application, so I want my application to continue normal execution after calling this (so this should be non-blocking). I want this to run on a scheduled thread every X seconds. To do so I have the following:

    def start(self): 
        sync_thread = threading.Timer(30, self.readMessages(self.queue, self.client))
        sync_thread.start()

where queue and client are initialized in the __init__ function and assigned to self. The first start for the thread and invocation to my readMessages function works well, though currently after 30 seconds I get the following error:

Exception in thread Thread-4:
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.8/threading.py", line 1254, in run
    self.function(*self.args, **self.kwargs)
TypeError: 'NoneType' object is not callable

Any idea what to do with it, or is there a better way to do this?

CodePudding user response:

You are getting the TypeError: 'NoneType' object is not callable because the threading.Timer routine is trying to call the function you passed, however, as @CherryDT mentionned in the comments, you didn't pass any function, rather you passed the return value of the readMessage method in your class.

What you need to do instead is:

def MinPrinter:
    def __init__(self, xs):
        self.xs = xs
    
    def callback(self, xs): # This is your 'readMessages' function
        print(min(xs))
   
    def start(self, n):
        t = threading.Timer(n, self.callback, args=(self.xs,))
        t.start()


mp = MinPrinter([1, 2, 0, 3, -1])
mp.start(30)

# Will print after 30 seconds
-1

Arguments need to be specified with the args parameter. Optionnaly, you can also pass keyword arguments using the kwargs parameter (need to pass a dictionnary).

CodePudding user response:

try

def start(self): 
    sync_thread = threading.Timer(30, self.readMessages, args=(self.queue, 
    self.client))
    sync_thread.start()

you should pass a callable object to timer and when you use self.readMessages(self.queue, self.client) you are executing the function and not passing it.

and anyway using Timer will not give you what you want. it will call the function self.readMessages once after 30 seconds but it will not repeat every 30 seconds.

a (very) simple way of doing it

from threading import Thread 
from sched import scheduler
import time

class SchedTask(Thread):
    def __init__(self, interval, task, task_args=()):
        super().__init__()
        self.scd = scheduler(time.time, time.sleep)
        self.interval = interval
        self.task = task
        self.task_args = task_args
    
    def schedual_task(self):
        self.scd.enter(self.interval, 1, self.schedual_task)
        self.task(self.task_args)
        self.scd.run()

    def run(self):
        self.schedual_task()

Then in you code you can use it like that

def t(*args):
    print("i am a task")

st = SchedTask(5,t)
st.start()
#rest of your code
...

there are many ways of doing this but this is just a simple example.

  • Related