Is it bad form to exit() from a function? - python

Is it bad form to exit() from within function?
def respond_OK():
sys.stdout.write('action=OK\n\n')
sys.stdout.flush() # redundant when followed by exit()
sys.exit(0)
Rather than setting an exit code and exit()ing from the __main__ name space?
def respond_OK():
global exit_status
sys.stdout.write('action=OK\n\n')
sys.stdout.flush()
exit_status = 0
sys.exit(exit_status)
The difference is negligible from a function perspective, just wondered what the consensus is on form. If you found the prior in someone else's code, would you look at it twice?

I would prefer to see an exception raised and handled from a main entry point, the type of which is translated into the exit code. Subclassing exceptions is so simple in python it's almost fun.
As posted in this answer's comments: Using sys.exit also means that the point of termination needs to know the actual status code, as opposed to the kind of error it encountered. Though that could be solved by an set of constants, of course. Using exceptions has other advantages, though: if one method fails, you could try another without re-entry, or print some post-mortem debugging info.

It makes no difference in terms of functionality, but it will likely make your code harder to follow, unless you take appropriate steps, e.g. commenting each of the calls from the main namespace which could lead to an exit.
Update: Note #mgilson's answer re the effect of catching an exception [It is possible to catch the exception that system.exit raises, and thus prevent exit]. You could make your code even more confusing that way.
Update 2: Note #sapht's suggestion to use an exception to orchestrate an exit. This is good advice, if you really want to do a non-local exit. Much better than setting a global.

There are a few cases where it's reasonably idiomatic.
If the user gives you bad command-line arguments, instead of this:
def usage(arg0):
print ... % (arg0,)
return 2
if __name__ == '__main__':
if ...:
sys.exit(usage(sys.argv[0]))
You often see this:
def usage():
print ... % (sys.argv[0],)
sys.exit(2)
if __name__ == '__main__':
if ...:
usage()
The only other common case I can think of is where initializing some library (via ctypes or a low-level C extension module) fails unexpectedly and leaves you in a state you can't reason about, so you just want to get out as soon as possible (e.g., to reduce the chance of segfaulting or printing garbage) For example:
if libfoo.initialize() != 0:
sys.exit(1)
Some might object to that because sys.exit doesn't actually bail out of the interpreter as soon as possible (it throws and catches an exception), so it's a false sense of safety. But you still see it reasonably often.

Related

Pythonic way to resume after dealing with Exception

I have a script that is operating a physical device. There is a physical malfunction on the device that occurs sometimes and when it does, I want to reset the device and continue with the script. I have this:
while True:
do_device_control()
device_status = get_device_status()
if device_status == 'Fault':
reset_device()
It seems to me that a neater / more Pythonic approach would raise an Exception:
try:
while True:
do_device_control()
device_status = get_device_status()
if device_status == 'Fault':
raise DeviceFaultException()
except DeviceFaultException:
reset_device()
But as far as I can see, there is no way to resume the script after resetting the device. Is there a way to make Exception handling work for this situation, or a more Pythonic approach than what I'm currently using?
A common Python idiom is "Ask forgiveness rather than permission", which applies very well to your question. An implementation like the following would suffice:
while True:
try:
do_device_control()
except DeviceFaultException:
reset_device()
This should get similar behavior as to what is in your original block of code using if statements.
However, you probably noticed that I did not check the device_status in my code. This is because you should allow the do_device_control function to raise the exception if it is unable to complete its task. This allows you to handle exceptions at the time the actions are being executed.
In an environment where you are working with a device that is running asynchronously to your code, you may check the device status and it be fine. It then might fail between the check and your do_device_control function. This is one example of why the "ask forgiveness rather than permission" paradigm exists.
Of course, this example only works if you can expect the do_device_control function to throw some sort of exception or change it so that it does. If you cannot expect/change this behavior, your first code block with the if statement would be preferred over explicitly raising an exception just to immediately catch it within the same code block.

Execute when error occurs python

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

Is it reasonable to wrap an entire main loop in a try..finally block?

I've made a map editor in Python2.7.9 for a small project and I'm looking for ways to preserve the data I edit in the event of some unhandled exception. My editor already has a method for saving out data, and my current solution is to have the main loop wrapped in a try..finally block, similar to this example:
import os, datetime #..and others.
if __name__ == '__main__':
DataMgr = DataManager() # initializes the editor.
save_note = None
try:
MainLoop() # unsurprisingly, this calls the main loop.
except Exception as e: # I am of the impression this will catch every type of exception.
save_note = "Exception dump: %s : %s." % (type(e).__name__, e) # A memo appended to the comments in the save file.
finally:
exception_fp = DataMgr.cwd + "dump_%s.kmap" % str(datetime.datetime.now())
DataMgr.saveFile(exception_fp, memo = save_note) # saves out to a dump file using a familiar method with a note outlining what happened.
This seems like the best way to make sure that, no matter what happens, an attempt is made to preserve the editor's current state (to the extent that saveFile() is equipped to do so) in the event that it should crash. But I wonder if encapsulating my entire main loop in a try block is actually safe and efficient and good form. Is it? Are there risks or problems? Is there a better or more conventional way?
Wrapping the main loop in a try...finally block is the accepted pattern when you need something to happen no matter what. In some cases it's logging and continuing, in others it's saving everything possible and quitting.
So you're code is fine.
If your file isn't that big, I would suggest maybe reading the entire input file into memory, closing the file, then doing your data processing on the copy in memory, this will solve any problems you have with not corrupting your data at the cost of potentially slowing down your runtime.
Alternatively, take a look at the atexit python module. This allows you to register a function(s) for a automatic callback function when the program exits.
That being said what you have should work reasonably well.

How to test while-loop (once) with nosetest (Python 2.7)

I'm pretty new to this whole "programming thing" but at age 34 I thought that I'd like to learn the basics.
I unfortunately don't know any python programmers. I'm learning programming due to personal interest (and more and more for the fun of it) but my "social habitat" is not "where the programmers roam" ;) .
I'm almost finished with Zed Shaws "Learn Python the Hard Way" and for the first time I can't figure out a solution to a problem. The last two days I didn't even stumble upon useful hints where to look when I repeatedly rephrased (and searched for) my question.
So stackoverflow seems to be the right place.
Btw.: I lack also the correct vocabular quite often so please don't hesitate to correct me :) . This may be one reason why I can't find an answer.
I use Python 2.7 and nosetests.
How far I solved the problem (I think) in the steps I solved it:
Function 1:
def inp_1():
s = raw_input(">>> ")
return s
All tests import the following to be able to do the things below:
from nose.tools import *
import sys
from StringIO import StringIO
from mock import *
import __builtin__
# and of course the module with the functions
Here is the test for inp_1:
import __builtin__
from mock import *
def test_inp_1():
__builtin__.raw_input = Mock(return_value="foo")
assert_equal(inp_1(), 'foo')
This function/test is ok.
Quite similar is the following function 2:
def inp_2():
s = raw_input(">>> ")
if s == '1':
return s
else:
print "wrong"
Test:
def test_inp_2():
__builtin__.raw_input = Mock(return_value="1")
assert_equal(inp_1(), '1')
__builtin__.raw_input = Mock(return_value="foo")
out = StringIO()
sys.stdout = out
inp_1()
output = out.getvalue().strip()
assert_equal(output, 'wrong')
This function/test is also ok.
Please don't assume that I really know what is happening "behind the scenes" when I use all the stuff above. I have some layman-explanations how this is all functioning and why I get the results I want but I also have the feeling that these explanations may not be entirely true. It wouldn't be the first time that how I think sth. works turns out to be different after I've learned more. Especially everything with "__" confuses me and I'm scared to use it since I don't really understand what's going on. Anyway, now I "just" want to add a while-loop to ask for input until it is correct:
def inp_3():
while True:
s = raw_input(">>> ")
if s == '1':
return s
else:
print "wrong"
The test for inp_3 I thought would be the same as for inp_2 . At least I am not getting error messages. But the output is the following:
$ nosetests
......
# <- Here I press ENTER to provoke a reaction
# Nothing is happening though.
^C # <- Keyboard interrupt (is this the correct word for it?)
----------------------------------------------------------------------
Ran 7 tests in 5.464s
OK
$
The other 7 tests are sth. else (and ok).
The test for inp_3 would be test nr. 8.
The time is just the times passed until I press CTRL-C.
I don't understand why I don't get error- or "test failed"-meassages but just an "ok".
So beside the fact that you may be able to point out bad syntax and other things that can be improved (I really would appreciate it, if you would do this), my question is:
How can I test and abort while-loops with nosetest?
So, the problem here is when you call inp_3 in test for second time, while mocking raw_input with Mock(return_value="foo"). Your inp_3 function runs infinite loop (while True) , and you're not interrupting it in any way except for if s == '1' condition. So with Mock(return_value="foo") that condition is never satisfied, and you loop keeps running until you interrupt it with outer means (Ctrl + C in your example). If it's intentional behavior, then How to limit execution time of a function call in Python will help you to limit execution time of inp_3 in test. However, in cases of input like in your example, developers often implement a limit to how many input attempts user have. You can do it with using variable to count attempts and when it reaches max, loop should be stopped.
def inp_3():
max_attempts = 5
attempts = 0
while True:
s = raw_input(">>> ")
attempts += 1 # this is equal to "attempts = attempts + 1"
if s == '1':
return s
else:
print "wrong"
if attempts == max_attempts:
print "Max attempts used, stopping."
break # this is used to stop loop execution
# and go to next instruction after loop block
print "Stopped."
Also, to learn python I can recommend book "Learning Python" by Mark Lutz. It greatly explains basics of python.
UPDATE:
I couldn't find a way to mock python's True (or a builtin.True) (and yea, that sounds a bit crazy), looks like python didn't (and won't) allow me to do this. However, to achieve exactly what you desire, to run infinite loop once, you can use a little hack.
Define a function to return True
def true_func():
return True
, use it in while loop
while true_func():
and then mock it in test with such logic:
def true_once():
yield True
yield False
class MockTrueFunc(object):
def __init__(self):
self.gen = true_once()
def __call__(self):
return self.gen.next()
Then in test:
true_func = MockTrueFunc()
With this your loop will run only once. However, this construction uses a few advanced python tricks, like generators, "__" methods etc. So use it carefully.
But anyway, generally infinite loops considered to be bad design solutions. Better to not getting used to it :).
It's always important to remind me that infinite loops are bad. So thank you for that and even more so for the short example how to make it better. I will do that whenever possible.
However, in the actual program the infinite loop is how I'd like to do it this time. The code here is just the simplified problem.
I very much appreciate your idea with the modified "true function". I never would have thought about that and thus I learned a new "method" how tackle programming problems :) .
It is still not the way I would like to do it this time, but this was the so important clue I needed to solve my problem with existing methods. I never would have thought about returning a different value the 2nd time I call the same method. It's so simple and brilliant it's astonishing me :).
The mock-module has some features that allows a different value to be returned each time the mocked method is called - side effect .
side_effect can also be set to […] an iterable.
[when] your mock is going to be
called several times, and you want each call to return a different
value. When you set side_effect to an iterable every call to the mock
returns the next value from the iterable:
The while-loop HAS an "exit" (is this the correct term for it?). It just needs the '1' as input. I will use this to exit the loop.
def test_inp_3():
# Test if input is correct
__builtin__.raw_input = Mock(return_value="1")
assert_equal(inp_1(), '1')
# Test if output is correct if input is correct two times.
# The third time the input is corrct to exit the loop.
__builtin__.raw_input = Mock(side_effect=['foo', 'bar', '1'])
out = StringIO()
sys.stdout = out
inp_3()
output = out.getvalue().strip()
# Make sure to compare as many times as the loop
# is "used".
assert_equal(output, 'wrong\nwrong')
Now the test runs and returns "ok" or an error e.g. if the first input already exits the loop.
Thank you very much again for the help. That made my day :)

Trapping an error in python

I am trying to trap error and let the code finish running.
In the code below, I "do Something." if fails, I want to print Error Msg
and continue running the second half.
What is happening is When an error occurs with the first section, The error statement print and
stops running. I would like the code to keep running past the first section.
if len(rows) > 0:
try:
print "Do something"
except:
print time.strftime("%H:%M:%S")
try:
print "Do somethings else"
except:
print time.strftime("%H:%M:%S")
Python's exceptions don't have a built-in restart capability to "continue running the second half". Instead, you just need to move the "unconditional, always do this" part out of the try-clause and into a finally-clause or outside the try-statement altogether.
P.S. It is usually ill-advised to swallow all exceptions with a bare except-clause. Instead, the usual best practice is to catch only the exceptions you know how to handle.
Can you programmatically determine if 'do Something' failed? If so, that's a better way to go rather than just relying on an exception handling mechanism. I see this anti-pattern a lot in .net code. Exceptions, in many languages, are intended for exceptional circumstances, not just error handling.
Keeping answer just to preserve comments for those who might think like I did.

Categories

Resources