Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python sched.scheduler exceeds max recursion depth

I have recently started learning Python and part of the simple app I am making includes a timer with a hh:mm:ss display running in its own thread.

Looking around the web I found two ways of implementing this:

  1. Using sched.scheduler
  2. Using threading.Timer

The way I did it looks similar for both implementations:

sched:

def tick(self, display, alarm_time):

    # Schedule this function to run every minute
    s = sched.scheduler(time.time, time.sleep)
    s.enter(1, 1, self.tick, ([display, alarm_time]))

    # Update the time
    self.updateTime(display)

Timer:

def tick(self, display):

    # Schedule this function to run every second
    t = Timer(1, self.tick, (display,alarm_time))
    t.start()

    # Update the time
    self.updateTime(display)
  1. Works fine with regards to ticking correctly, but generates the following error after a few minutes: RuntimeError: maximum recursion depth exceeded. I know you can increase the max recursion level manually, but surely this should not be necessary here?

  2. No error, but occasionally the seconds will skip, or tick irregularly.

Can someone please point me in the right direction as to how to do this correctly? Thank you.

like image 805
user171331 Avatar asked Sep 10 '09 10:09

user171331


People also ask

How do you fix maximum recursion depth exceeded?

The maximum recursion depth in Python is 1000. You can change the limit by calling sys. setrecursionlimit() method.

Is there any limit for recursion in Python?

Due to this, the recursion limit of python is usually set to a small value (approx, 10^4). This means that when you provide a large input to the recursive function, you will get an error. This is done to avoid a stack overflow. The Python interpreter limits the recursion limit so that infinite recursions are avoided.

How do I remove a recursion error in Python?

Try increasing the recursion limit ( sys. setrecursionlimit ) or re-writing your code without recursion. Return the current value of the recursion limit, the maximum depth of the Python interpreter stack. This limit prevents infinite recursion from causing an overflow of the C stack and crashing Python.

What is the maximum depth of recursive calls a function?

The maximal number of nested calls (including the first one) is called recursion depth. In our case, it will be exactly n . The maximal recursion depth is limited by JavaScript engine. We can rely on it being 10000, some engines allow more, but 100000 is probably out of limit for the majority of them.


2 Answers

Here's how to make a one-shot into a periodic event, e.g. with sched: if the function must make its own scheduler and be the only thing running on its thread:

def tick(self, display, alarm_time, scheduler=None):
  # make a new scheduler only once & schedule this function immediately
  if scheduler is None:
    scheduler = sched.scheduler(time.time, time.sleep)
    scheduler.enter(0, 1, self.tick, ([display, alarm_time, scheduler]))
    scheduler.run()

  # reschedule this function to run again in a minute
  scheduler.enter(1, 1, self.tick, (display, alarm_time, scheduler]))

  # do whatever actual work this function requires, e.g.:
  self.updateTime(display)

If other events must also be scheduled in the same thread then the scheduler must be made and owned "elsewhere" -- the if part above can get refactored into another method, e.g.:

def scheduleperiodic(self, method, *args):
  self.scheduler = sched.scheduler(time.time, time.sleep)
  self.scheduler.enter(0, 1, method, args)
  # whatever else needs to be scheduled at start, if any, can go here
  self.scheduler.run()

def tick(self, display, alarm_time):
  # reschedule this function to run again in a minute
  self.scheduler.enter(60, 1, self.tick, (display, alarm_time))

  # do whatever actual work this function requires, e.g.:
  self.updateTime(display)

Again, of course and as always with sched, while the scheduler is running, it (and the scheduled event callbacks) will "take over" the thread in question (so you'll need to hive off a separate thread for it if you need other things to be happening at the same time).

If you need to use this kind of idiom in many functions it could be refactored into a decorator, but that would somewhat mask the underlying simplicity of the idiom, so I prefer this simple, overt use. BTW, note that time.time and time.sleep use seconds, not minutes, as their unit of time, so you need 60, not one, to indicate "a minute from now";-).

like image 151
Alex Martelli Avatar answered Nov 03 '22 05:11

Alex Martelli


A Timer is a one-shot event. It cannot be made to loop this way.

Using a Timer to call a function which then creates another Timer which calls a function that creates a Timer which calls a function which creates a Timer, ..., must reach the recursion limit.

You don't mention your OS, but the "skipping" or "ticking irregularly" is for two reasons.

  1. You computer is busy and "1 second" means "pretty close to 1 second, depending on what else is going on"

  2. If you start your timer at 0.9999 seconds, and wait 1 second, you might be at 1.9999 (rounds down to 1) or 2.00000. It may appear to duplicate a time or skip a time. Your computer's internal hardware clock is very accurate, and rounding things off to the nearest second will (always) lead to the remote possibility of duplicates or skips.

Use sched correctly. http://docs.python.org/library/sched.html#module-sched

Your code snippet makes no sense for sched, either. You do not need to create a new scheduler object. You only need to create a new event.

Read http://docs.python.org/library/sched.html#sched.scheduler.enter on creating a new event for an existing scheduler instance.

like image 29
S.Lott Avatar answered Nov 03 '22 06:11

S.Lott