Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

set python recursion limit for a function

I have 2 solutions to a recursion problem that I need for a function (actually a method). I want it to be recursive, but I want to set the recursion limit to 10 and reset it after the function is called (or not mess with recursion limit at all). Can anyone think of a better way to do this or recommend using one over the others? I'm leaning towards the context manager because it keeps my code cleaner and no setting the tracebacklimit, but there might be caveats?

import sys

def func(i=1):
    print i
    if i > 10:
        import sys
        sys.tracebacklimit = 1
        raise ValueError("Recursion Limit")
    i += 1
    func(i)

class recursion_limit(object):
    def __init__(self, val):
        self.val = val
        self.old_val = sys.getrecursionlimit()
    def __enter__(self):
        sys.setrecursionlimit(self.val)
    def __exit__(self, *args):
        sys.setrecursionlimit(self.old_val)
        raise ValueError("Recursion Limit")

def func2(i=1):
    """
    Call as

    with recursion_limit(12):
        func2()
    """
    print i
    i += 1
    func2(i)

if __name__ == "__main__":
    #    print 'Running func1'
    #    func()

    with recursion_limit(12):
        func2()

I do see some odd behavior though with the context manager. If I put in main

with recursion_limit(12):
    func2()

It prints 1 to 10. If I do the same from the interpreter it prints 1 to 11. I assume there is something going on under the hood when I import things?

EDIT: For posterity this is what I have come up with for a function that knows its call depth. I doubt I'd use it in any production code, but it gets the job done.

import sys
import inspect
class KeepTrack(object):
    def __init__(self):
        self.calldepth = sys.maxint

    def func(self):
        zero = len(inspect.stack())
        if zero < self.calldepth:
            self.calldepth = zero
        i = len(inspect.stack())
        print i - self.calldepth
        if i - self.calldepth < 9:
            self.func()

keeping_track = KeepTrack()
keeping_track.func()
like image 267
jseabold Avatar asked Aug 04 '11 17:08

jseabold


People also ask

How do you set a recursion limit in Python?

The Python interpreter limits the recursion limit so that infinite recursions are avoided. The “sys” module in Python provides a function called setrecursionlimit() to modify the recursion limit in Python. It takes one parameter, the value of the new recursion limit.

How do you limit recursion?

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.

How do I bypass Python recursion limit?

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

Can you have recursion of any depth?

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.


3 Answers

You shouldn't change the system recursion limit at all. You should code your function to know how deep it is, and end the recursion when it gets too deep.

The reason the recursion limit seems differently applied in your program and the interpreter is because they have different tops of stack: the functions invoked in the interpreter to get to the point of running your code.

like image 108
Ned Batchelder Avatar answered Nov 04 '22 15:11

Ned Batchelder


While somewhat tangential (I'd have put it in a comment, but I don't think there's room), it should be noted that setrecursionlimit is somewhat misleadingly named - it actually sets the maximum stack depth:

http://docs.python.org/library/sys.html#sys.setrecursionlimit

That's why the function behaves differently depending on where you call it from. Also, if func2 were to make a stdlib call (or whatever) that ended up calling a number of functions such that it added more than N to the stack, the exception would trigger early.

Also also, I wouldn't change the sys.tracebacklimit either; that will have an effect on the rest of your program. Go with Ned's answer.

like image 28
Eli Stevens Avatar answered Nov 04 '22 17:11

Eli Stevens


ignoring the more general issues, it looks like you can get the current frame depth by looking at the length of inspect.getouterframes(). that would give you a "zero point" from which you can set the depth limit (disclaimer: i haven't tried this).

edit: or len(inspect.stack()) - it's not clear to me what the difference is. i would be interested in knowing if this works, and whether they were different.

like image 1
andrew cooke Avatar answered Nov 04 '22 15:11

andrew cooke