The code
def Recursion():
try:
raise RecursionError()
except RecursionError:
Recursion()
Recursion()
crashes.
I am aware that this is a recursive function, but shouldn't Python just raise an StackError, instead of just crashing? If someone could explain this, I would be very grateful!
Something similar has been reported as a bug: https://bugs.python.org/issue42509, with someone commenting that
Catching a RecursionError might be fine sometimes, but the issue is that Program 1 catches a RecursionError and then keeps recursing more rather than stopping.
I think it might have to be the responsibility of the Python user to make sure that if a RecursionError is to be caught, that the program can recover without making things much worse. It's my understanding that the extra buffer of +50 is to make sure that the programmer has room to stop the overflow and do any necessary cleanup [1].
If no attempt is made to clean up, then it seems reasonable to me that Python should crash, unless there's some idea of what could happen that I'm missing. The interpreter could allow arbitrary recursion during the cleanup until the C stack overflows, but that sort of defeats the point of the recursion checker. It could raise some new ExtraSuperRecurionError, but that doesn't fix anything: what if that error is caught ;) ?
https://bugs.python.org/msg382128
Assuming this is a minimally reproducible toy example for a real issue you're facing, you should do:
def Recursion():
try:
raise Exception()
except RecursionError:
raise
except Exception:
Recursion()
Recursion()
The RecursionError that you see is not the one you manually raise. It does not occur within the try-block and therefore is not caught:
def Recursion():
try:
raise RecursionError() # does not happen here ...
except RecursionError:
Recursion() # ... but here where it goes unhandled
Related
I guess I'm not the first asking this question, but I haven't found a solution that I could use/understand yet. And the issue is probably not as simple as i first expected.
I think it can be boiled down to two general questions:
1) Is there a way to avoid Python to stop when an error occur and just jump on to the next line of code in the script?
2) Is there a way to make Python execute a line of code if an error occurs? Like, if error then...
My concrete problem:
I have a very large program with a lot of functions and other stuff, which would take forever to adjust individually by using "try" for example (if i understand it correctly)
My program run as a large loop that gather information and keeps running. This means that it does not really matter to me, that my program fails multiple time as long as it keeps running. I can easily handle that some of the information is with error and would just like my program to take a note of it and keep going.
Is there a solution to this?
As you rightly pointed out, the try/catch block in Python is by far your best ally:
for i in range(N):
try: do_foo() ; except: do_other_foo()
try: do_bar() ; except: do_other_bar()
Alternatively, you could also use, in case you didn't need the Exception:
from contextlib import suppress
for i in range(N):
with suppress(Exception):
do_foo()
with suppress(Exception):
do_bar()
Your only possibility is to rely on the try/except clause. Keep in mind that the try/except may use also finally and else (see documentation:
try:
print("problematic code - error NOT raised")
except:
print("code that gets executed only if an error occurs")
else:
print("code that gets executed only if an error does not occur")
finally:
print("code that gets ALWAYS executed")
# OUTPUT:
# problematic code - error NOT raised
# code that gets executed only if an error does not occur
# code that gets ALWAYS executed
or, when an error is raised:
try:
print("problematic code - error raised!")
raise "Terrible, terrible error"
except:
print("code that gets executed only if an error occurs")
else:
print("code that gets executed only if an error does not occur")
finally:
print("code that gets ALWAYS executed")
# OUTPUT:
# problematic code - error raised!
# code that gets executed only if an error occurs
# code that gets ALWAYS executed
I urge to point out, by the way, that ignoring everything makes me shiver:
you really should (at least, more or less) identify which exception can be raised, catch them (except ArithmeticError: ..., check built-in exceptions) and handle them individually. What you're trying to do will probably snowball into an endless chain of problems, and ignoring them will probably create more problems!
I think that this question helps to understand what a robust software is, meanwhile on this one you can see how SO community thinks python exceptions should be handled
For example i have a program with this structure:
Domain logic module -> Settings module -> Settings store backend
Next is a part of Settings module.
def load_from_json(self, json_str):
try:
self.load_from_dict(json.loads(json_str))
except ValueError as e:
raise SettingsLoadDataException('Error loading json')
Need I a custom exception SettingsLoadDataException here, or I could just skip catching json.loads errors?
def load_from_json(self, json_str):
self.load_from_dict(json.loads(json_str))
Update.
Also good variant is:
def load_from_json(self, json_str):
try:
self.load_from_dict(json.loads(json_str))
except ValueError as e:
raise ValueError('Error loading json')
That is a problem only you can answer. You could catch all exceptions, or you could let the program crash if it throws an exception you don't handle. If it is vital that the program doesn't crash, catch the exception. However, you should implement a recovery method then. If the Json doesn't load properly, can your program do anything useful without it ? If it can, I would catch the exception, otherwise you could just display an error and terminate.
You should work with exceptions in such a way, that seeing a stack trace explains the problem to you immediately.
I am no Python expert, but won't you loose the piece of information that it was actually ValueError, that caused program crash? You will see only SettingsLoadDataException in a trace without any real reason of it, right?
Also, if you do not rethrow exceptions, you should catch only those, you know how to deal with. It is always better to have your program crash, than to leave it in an unexpected state.
I wrote a fibonacci function that infinitely recurses, and while python couldn't detect it and threw errors upon hitting the max recursion limit, when I used try and assert to see if fib(30) was equal to some value, it immediately told me it was not. How did it do this? It seems like it didn't even need to run fib(30).
note:
I realize this only works if I do
try:
assert infiniteFib(30) == 832040
except:
print "done immediately"
When I do just the assert, it produces many errors about too many recursions, but with the try it stops at the first error.
What I'm curious is how does python manage to produce an error so quickly about infinite recursion? Wouldn't it need to hit the limit (which takes a long time) to tell whether it was infinite?
EDIT:
Some requested code, but just to be clear, I DON'T want a solution to the errors (I know it's wrong because I deliberately excluded the base case), I want to know how python produces errors so quickly when it should take much longer (if you do fib(30), clearly it takes a while to hit the max recursion limit, but somehow python produces errors way before then):
def fib(n):
return fib(n-1) + fib(n-2)
try: assert(fib(30) == 832040)
except: print "done immediately"
The reason the code you've shown runs quickly is because it catches the exception that is raised by fib when it hits the recursion limit and doesn't print the traceback. Running to the recursion limit doesn't take very long at all, but formatting and printing hundreds of lines of traceback does.
If you inspect the exception you get, you'll see it is the same RuntimeError you get when you run fib normally, not an AssertionError. Try this, to see what is going on better:
try:
assert(fib(30) == 832040)
except Exception as e:
print("Got an Exception: %r" % e)
It's not done immediately. Your code runs until python reaches maximum recursion depth and maximum recursion depth is set to 1000 by default in python to avoid stack overflow errors.
So, actually your code runs till it reaches recursion depth of 1000 and errors out RuntimeError: maximum recursion depth exceeded. You can verify this by modifying your code as below:
i=0
def fib(n):
global i
i = i + 1
print i
return fib(n-1) + fib(n-2)
assert(fib(30) == 832040)
print i
print "done immediately"
In my machine, i am getting the last i value as 984 before errors out.
Is there a way to test when a program's recursion limit is reached? For example:
def funct():
print "Hello"
#Some code for testing the recursion limit.
#Some more code to execute
If you are wondering why I want to know this, here is why: I want to hide an Easter egg in a program, but I can't do that until I found out how to do what I am asking. So, how can I test when the recursion limit is reached? Is there even a way?
You can test for the RuntimeError like any other error, with try:
def funct():
...
try:
funct()
except RuntimeError as e:
if "recursion" in e.message:
print("Easter egg!")
Note that I've added an extra check that the error is warning about recursion, so you don't prevent any other RuntimeErrors from going about their business.
I want to know what is the best way of checking an condition in Python definition and prevent it from further execution if condition is not satisfied. Right now i am following the below mentioned scheme but it actually prints the whole trace stack. I want it to print only an error message and do not execute the rest of code. Is there any other cleaner solution for doing it.
def Mydef(n1,n2):
if (n1>n2):
raise ValueError("Arg1 should be less than Arg2)
# Some Code
Mydef(2,1)
That is what exceptions are created for. Your scheme of raising exception is good in general; you just need to add some code to catch it and process it
try:
Mydef(2,1)
except ValueError, e:
# Do some stuff when exception is raised, e.message will contain your message
In this case, execution of Mydef stops when it encounters raise ValueError line of code, and goes to the code block under except.
You can read more about exceptions processing in the documentation.
If you don't want to deal with exceptions processing, you can gracefully stop function to execute further code with return statement.
def Mydef(n1,n2):
if (n1>n2):
return
def Mydef(n1,n2):
if (n1>n2):
print "Arg1 should be less than Arg2"
return None
# Some Code
Mydef(2,1)
Functions stop executing when they reach to return statement or they run the until the end of definition. You should read about flow control in general (not specifically to python)