How to catch errors elegantly and keep methods clean? - python

I am in the process of writing a small(er) Python script to automate a semi-frequent, long, and error-prone task. This script is responsible for making various system calls - either though os.system or through os.(mkdir|chdir|etc).
Here is an example of my code right now:
class AClass:
def __init__(self, foo, bar, verbose=False, silent=False):
# Sets up variables needed for each instance, etc
self.redirect = ''
if silent:
self.redirect = '> 2>&1'
self.verbose = verbose
def a_method(self):
""" Responsible for running 4-6 things via system calls as described """
if self.verbose and not self.silent:
print "Creating a directory"
try:
os.mkdir('foobar')
except OSError, e:
raise OSError, "Problem creating directory %s: %s" % (e.filename, e.strerror)
if self.verbose and not self.silent:
print "Listing a directory"
if (os.system('ls foobar %s') % self.redirect) is not 0:
raise OSError, "Could not list directory foobar"
def b_method(self):
""" Looks very similar to a_method() """
def run(self):
""" Stitches everything together """
try:
a_method()
except OSError, e:
print "a_method(): %s" % e.strerror
sys.exit(-1)
try:
b_method()
except OSError, e:
print "b_method(): %s" % e.strerror
sys.exit(-1)
Obviously writing all the if self.verbose and not self.silent is messy and then the try/catch or if around each call is ugly to look at.
I would have liked to use Python's logging class and simply have one logging level (verbose) configurable via command line and then I can simple call logger.debug('My message') but I am using Python 2.2 and I do not have access to the logging class.
Summary/Base Questions
I am using Python 2.2 and I cannot change this. I am running on an ESX 3.0.2 server and I cannot touch it in any other way for the time being.
What is the best way to handle error checking and verbose output without tying this logic to your class (which should only do One Thing)?
How can I reduce the clutter with something more simple or elegant to look at?
Thanks!

writing all the if verbose and not silent is messy
So, instead, assign sys.stdout to a dummy class whose write is a no-op if you need to be unverbose or silent, then just use print without needing guards. (Do remember to restore sys.stdout to the real thing for prints that aren't so conditioned -- easier to encapsulate in a couple of functions, actually).
For error checks, all the blocks like:
try:
a_method()
except OSError, e:
print "a_method(): %s" % e.strerror
sys.exit(-1)
can and should be like
docall(a_method)
for what I hope is a pretty obvious def docall(acallable):.
Similarly, other try/except case and ones where the new exception is raised conditionally can become calls to functions with appropriate arguments (including callables, i.e. higher order functions). I'll be glad to add detailed code if you clarify what parts of this are hard or unclear to you!
Python 2.2, while now very old, was a great language in its way, and it's just as feasible to use it neatly, as you wish, as it would be for other great old languages, like, say, MacLisp;-).

How to clean up your verbose output
Move the verbose/quiet logic into a single function, and then call that function for all of your output. If you make it something nice and short it keeps your mainline code quite tidy.
def P(s):
if (verbose):
print s
I have a package that does this in our internal code, it has the following methods:
P -- normal print: P('this prints regardless, --quiet does not shut it up')
V -- verbose print: V('this only prints if --verbose')
D -- debug print: D('this only prints if --debug')

Related

Code Coverage: How to write 100% coverage tests in pytest for functions having except KeyboardInterrupt blocks?

I have a function which catches KeyboardInterrupts for additional functionalities and I have to write tests in pytest and I don't know how to create a testcase to cover the KeyboardInterrupt catch block.
The code is similar to this:
# main_game.py
class Cache:
other_details = dict()
def save_progress_for_future(progress):
file = open('progress.json', 'w')
content = {'progress_percent':progress, **other_details}
json.dump(content, file)
file.close()
def loadingBar():
progress = 0
while True:
try:
... # other stuff
progress = get_progress_percent()
print('\r Loading' + str('.' * progress//10) + '\r', end='')
except KeyboardInterrupt:
save_progress_in_file(progress)
How am I going to write tests in pytest in other tests file (say test_main_game.py) to cover the KeyboardInterrupt part, and so that coverage cli tool shows 100% of code coverage in its report?
Exceptions like keyboard interrupt signals, out of memory failures and such are generally non-deterministic, so one can't possibly have any guarantees if and when they will be even raised during the normal flow of execution. They originate at OS level rather than interpreter itself (unlike ValueError for instance). Given that, there is no reliable way to simulate those conditions to arise and align properly with the execution of a unit test code.
Now what you can do is to simulate the interrupt somewhere in your try block to raise an exception to redirect the execution to the code inside the except block. In order to do this, some code in # other stuff section or get_progress_percent() function should somehow raise the KeyboardInterrupt when under the unit test context.
Since it is unknown what is happening in the # other stuff, I'll stick with the get_progress_percent().
For this, a refactoring needs to be applied to a loadingBar() to make it accept a delegate to get_progress_percent() function, like so:
def loadingBar(progress_loader = get_progress_percent):
progress = 0
while (True):
try:
# other stuff
progress = progress_loader()
# print to stdout, etc...
except KeyboardInterrupt:
save_progress_in_file(progress)
Now if loadingBar() is called without arguments, it will assume the default value of progress_loader variable to be your default get_progress_percent() function. This is the actual call you make in your program.
To test the alternative flow inside the except block, you might consider to create additional unit test utilizing an overloaded call to loadingBar(), passing it the function which raises the KeyboardInterrupt.
Your unit test case might look like this:
import unittest
class LoadingBarTestCase(unittest.TestCase):
def testLoadingBar(self):
"""Test the code in try block"""
loadingBar()
# assertions for other stuff
# also assert that save_progress_in_file() doesn't get called
def testLoadingBarInterrupted(self):
"""Test the code in except block"""
# mock function to raise the interrupt
def interrupted_progress_loader():
raise KeyboardInterrupt()
# call loadingBar passing it a mock delegate
loadingBar(interrupted_progress_loader)
# assert that save_progress_in_file() got called by exception handler
if __name__ == '__main__':
unittest.main()
So, to wrap it up, some particular edge cases require that your code needs to be adjusted in a way to make it a bit more unit test friendly, which might not a bad thing at all.
I hope this helps 😃

Simple custom Exception do not raise the same way as bultins

I have a multiprocessed program - 4 processes, and inside these i spawn multiple threads so it is multithreaded as well. I do not have so much experience with tracing and debugging these, i am getting into it now. I have this code block in my tcp server though:
I have Exceptions like this
class Error(Exception):
"""Basic exception"""
class NoDataReceived(Error):
"""Raised when there is no data"""
Now the code
try:
...
if True:
raise ValueError('No data receieved') # <--- this will print
# raise NoDataReceived('There is no data') # <--- when i use this it won't print
except ValueError as err: # bultin
print('err')
except NoDataReceived as err: # custom
print('err') # Does NOT print..?
Now the thing is, when i raise ValueError, it prints out, when i raise the custom NoDataRecieved, it does not print at all.
I need to say it is multithreaded program and multiprocessed as well (threads within process), if i run this piece of code in singlethreaded simple script, it works as it should. I do not like this behaviour. Before i build anything on it i need to understand this better. I know i could use sys_traceback, but i really want to make this work as it should, simply.
Do you have an explanation for me my friends?
[Python 3.8]
Thank you for your time, Q.

How to catch when *any* error occurs in a whole script? [duplicate]

How do you best handle multiple levels of methods in a call hierarchy that raise exceptions, so that if it is a fatal error the program will exit (after displaying an error dialog)?
I'm basically coming from Java. There I would simply declare any methods as throws Exception, re-throw it and catch it somewhere at the top level.
However, Python is different. My Python code basically looks like the below.
EDIT: added much simpler code...
Main entry function (plugin.py):
def main(catalog):
print "Executing main(catalog)... "
# instantiate generator
gen = JpaAnnotatedClassGenerator(options)
# run generator
try:
gen.generate_bar() # doesn't bubble up
except ValueError as error:
Utilities.show_error("Error", error.message, "OK", "", "")
return
... usually do the real work here if no error
JpaAnnotatedClassGenerator class (engine.py):
class JpaAnnotatedClassGenerator:
def generate_bar(self):
self.generate_value_error()
def generate_value_error(self):
raise ValueError("generate_value_error() raised an error!")
I'd like to return to the caller with an exception that is to be thrown back to that ones call until it reaches the outermost try-except to display an error dialog with the exception's message.
QUESTION:
How is this best done in Python? Do I really have to repeat try-except for every method being called?
BTW: I am using Python 2.6.x and I cannot upgrade due to being bound to MySQL Workbench that provides the interpreter (Python 3 is on their upgrade list).
If you don't catch an exception, it bubbles up the call stack until someone does. If no one catches it, the runtime will get it and die with the exception error message and a full traceback. IOW, you don't have to explicitely catch and reraise your exception everywhere - which would actually defeat the whole point of having exceptions. Actually, despite being primarily used for errors / unexpected conditions, exceptions are first and foremost a control flow tool allowing to break out of the normal execution flow and pass control (and some informations) to any arbitrary place up in the call stack.
From this POV your code seems mostlt correct (caveat: I didn't bother reading the whole thing, just had a quick look), except (no pun indented) for a couple points:
First, you should define your own specific exception class(es) instead of using the builtin ValueError (you can inherit from it if it makes sense to you) so you're sure you only catch the exact exceptions you expect (quite a few layers "under" your own code could raise a ValueError that you didn't expect).
Then, you may (or not, depending on how your code is used) also want to add a catch-all top-level handler in your main() function so you can properly log (using the logger module) all errors and eventually free resources, do some cleanup etc before your process dies.
As a side note, you may also want to learn and use proper string formatting, and - if perfs are an issue at least -, avoid duplicate constant calls like this:
elif AnnotationUtil.is_embeddable_table(table) and AnnotationUtil.is_secondary_table(table):
# ...
elif AnnotationUtil.is_embeddable_table(table):
# ...
elif AnnotationUtil.is_secondary_table(table):
# ...
Given Python's very dynamic nature, neither the compiler nor runtime can safely optimize those repeated calls (the method could have been dynamically redefined between calls), so you have to do it yourself.
EDIT:
When trying to catch the error in the main() function, exceptions DON'T bubble up, but when I use this pattern one level deeper, bubbling-up seems to work.
You can easily check that it works correctly with a simple MCVE:
def deeply_nested():
raise ValueError("foo")
def nested():
return deeply_nested()
def firstline():
return nested()
def main():
try:
firstline()
except ValueError as e:
print("got {}".format(e))
else:
print("you will not see me")
if __name__ == "__main__":
main()
It appears the software that supplies the Python env is somehow treating the main plugin file in a wrong way. Looks I will have to check the MySQL Workbench guys
Uhu... Even embedded, the mechanism expection should still work as expected - at least for the part of the call stack that depends on your main function (can't tell what happens upper in the call stack). But given how MySQL treats errors (what about having your data silently truncated ?), I wouldn't be specially suprised if they hacked the runtime to silently pass any error in plugins code xD
It is fine for errors to bubble up
Python's exceptions are unchecked, meaning you have no obligation to declare or handle them. Even if you know that something may raise, only catch the error if you intend to do something with it. It is fine to have exception-transparent layers, which gracefully abort as an exception bubbles through them:
def logged_get(map: dict, key: str):
result = map[key] # this may raise, but there is no state to corrupt
# the following is not meaningful if an exception occurred
# it is fine for it to be skipped by the exception bubbling up
print(map, '[%s]' % key, '=>', result)
return result
In this case, logged_get will simply forward any KeyError (and others) that are raised by the lookup.
If an outer caller knows how to handle the error, it can do so.
So, just call self.create_collection_embeddable_class_stub the way you do.
It is fine for errors to kill the application
Even if nothing handles an error, the interpreter does. You get a stack trace, showing what went wrong and where. Fatal errors of the kind "only happens if there is a bug" can "safely" bubble up to show what went wrong.
In fact, exiting the interpreter and assertions use this mechanism as well.
>>> assert 2 < 1, "This should never happen"
Traceback (most recent call last):
File "<string>", line 1, in <module>
AssertionError: This should never happen
For many services, you can use this even in deployment - for example, systemd would log that for a Linux system service. Only try to suppress errors for the outside if security is a concern, or if users cannot handle the error.
It is fine to use precise errors
Since exceptions are unchecked, you can use arbitrary many without overstraining your API. This allows to use custom errors that signal different levels of problems:
class DBProblem(Exception):
"""Something is wrong about our DB..."""
class DBEntryInconsistent(DBProblem):
"""A single entry is broken"""
class DBInconsistent(DBProblem):
"""The entire DB is foobar!"""
It is generally a good idea not to re-use builtin errors, unless your use-case actually matches their meaning. This allows to handle errors precisely if needed:
try:
gen.generate_classes(catalog)
except DBEntryInconsistent:
logger.error("aborting due to corrupted entry")
sys.exit(1)
except DBInconsistent as err:
logger.error("aborting due to corrupted DB")
Utility.inform_db_support(err)
sys.exit(1)
# do not handle ValueError, KeyError, MemoryError, ...
# they will show up as a stack trace

Do we use try,except in every single function?

Should we always enclose every function we write with a try...except block?
I ask this because sometimes in one function we raise Exception, and the caller that calls this function doesn't have exception
def caller():
stdout, stderr = callee(....)
def callee():
....
if stderr:
raise StandardError(....)
then our application crash. In this obvious case, I am tempted to enclose callee and caller with try..except.
But I've read so many Python code and they don't do these try..block all the time.
def cmd(cmdl):
try:
pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
except Exception, e:
raise e
stdout, stderr = pid.communicate()
if pid.returncode != 0:
raise StandardError(stderr)
return (stdout, stderr)
def addandremove(*args,**kwargs):
target = kwargs.get('local', os.getcwd())
f = kwargs.get('file', None)
vcs = kwargs.get('vcs', 'hg')
if vcs is "hg":
try:
stdout, stderr = cmd(['hg', 'addremove', '--similarity 95'])
except StandardError, e:
// do some recovery
except Exception, e:
// do something meaningful
return True
The real thing that bothers me is this:
If there is a 3rd function that calls addandremove() in one of the statements, do we also surround the call with a try..except block? What if this 3rd function has 3 lines, and each function calls itself has a try-except? I am sorry for building this up. But this is the sort of problem I don't get.
Exceptions are, as the name implies, for exceptional circumstances - things that shouldn't really happen
..and because they probably shouldn't happen, for the most part, you can ignore them. This is a good thing.
There are times where you do except an specific exception, for example if I do:
urllib2.urlopen("http://example.com")
In this case, it's reasonable to expect the "cannot contact server" error, so you might do:
try:
urllib2.urlopen("http://example.com")
except urllib2.URLError:
# code to handle the error, maybe retry the server,
# report the error in a helpful way to the user etc
However it would be futile to try and catch every possible error - there's an inenumerable amount of things that could potentially go wrong.. As a strange example, what if a module modifies urllib2 and removes the urlopen attribute - there's no sane reason to expect that NameError, and no sane way you could handle such an error, therefore you just let the exception propagate up
Having your code exit with a traceback is a good thing - it allows you to easily see where the problem originate, and what caused it (based on the exception and it's message), and fix the cause of the problem, or handle the exception in the correct location...
In short, handle exceptions only if you can do something useful with them. If not, trying to handle all the countless possible errors will only make your code buggier and harder to fix
In the example you provide, the try/except blocks do nothing - they just reraise the exception, so it's identical to the much tidier:
def cmd(cmdl):
pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
stdout, stderr = pid.communicate()
if pid.returncode != 0:
raise StandardError(stderr)
return (stdout, stderr)
# Note: Better to use actual args instead of * and **,
# gives better error handling and docs from help()
def addandremove(fname, local = None, vcs = 'hg'):
if target is None:
target = os.getcwd()
if vcs is "hg":
stdout, stderr = cmd(['hg', 'addremove', '--similarity 95'])
return True
About the only exception-handling related thing I might expect is to handle if the 'hg' command isn't found, the resulting exception isn't particularly descriptive. So for a library, I'd do something like:
class CommandNotFound(Exception): pass
def cmd(cmdl):
try:
pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
except OSError, e:
if e.errno == 2:
raise CommandNotFound("The command %r could not be found" % cmdl)
else:
# Unexpected error-number in OSError,
# so a bare "raise" statement will reraise the error
raise
stdout, stderr = pid.communicate()
if pid.returncode != 0:
raise StandardError(stderr)
return (stdout, stderr)
This just wraps the potentially confusing "OSError" exception in the clearer "CommandNotFound".
Rereading the question, I suspect you might be misunderstanding something about how Python exceptions work (the "and the caller that calls this function doesn't have exception" bit, so to be hopefully clarify:
The caller function does not need any knowledge of the exceptions that might be raised from the children function. You can just call the cmd() function and hope it works fine.
Say your code is in a mystuff module, and someone else wants to use it, they might do:
import mystuff
mystuff.addandremove("myfile.txt")
Or, maybe they want to give a nice error message and exit if the user doesn't have hg installed:
import mystuff
try:
mystuff.addandremove("myfile.txt")
except mystuff.CommandNotFound:
print "You don't appear to have the 'hg' command installed"
print "You can install it with by... etc..."
myprogram.quit("blahblahblah")
You should use a try catch block so that you can specifically locate the source of the exception. You can put these blocks around anything you want, but unless they produce some sort of useful information, there is no need to add them.
try/except clauses are really only useful if you know how to handle the error that gets raised. Take the following program:
while True:
n=raw_input("Input a number>")
try:
n=float(n)
break
except ValueError:
print ("That wasn't a number!") #Try again.
However, you may have a function like:
def mult_2_numbers(x,y):
return x*y
and the user may try to use it as:
my_new_list=mult_2_numbers([7,3],[8,7])
The user could put this in a try/except block, but then my_new_list wouldn't be defined and would probably just raise an exception later (likely a NameError). In that case, you'd make it harder to debug because the line number/information in the traceback is pointing to a piece of code which isn't the real problem.
There are a couple of programming decisions for your coding team to make regarding introspection type tools and "exception" handling.
One good place to use exception handling is with Operating System calls such as file operations. Reasoning is, for example, a file may have it's access restricted from being accessed by the client appliction. That access restriction is typically an OS-admin task, not the Python application function. So exceptions would be a good use where you application does NOT have control.
You can mutate the previous paragraph's application of exceptions to be broader, in the sense of using Exceptions for things outside the control of what your team codes, to such things as all "devices" or OS resources like OS timers, symbolic links, network connections, etc.
Another common use case is when you use a library or package that is -designed- to through lots of exceptions, and that package expects you to catch or code for that. Some packages are designed to throw as few exceptions as possible and expect you to code based on return values. Then your exceptions should be rare.
Some coding teams use exceptions as a way to log fringe cases of "events" within your application.
I find when desiding whether to use exceptions I am either programing to minimize failure by not having a lot of try/except and have calling routines expect either valid return values or expect an invalid return values. OR I program for failure. That is to say, I program for expected failure by functions, which I use alot of try/except blocks. Think of programming for "expected" failure like working with TCP; the packets aren't garenteed to get there or even in order, but there are exception handling with TCP by use of send/read retrys and such.
Personally I use try-except blocks around the smallest possible block sizes, usually one line of code.
It's up to you; the main exceptions' roles involve (a quote from this splendid book):
Error handling
Event notification
Special-case handling
Termination actions
Unusual control flows
When you know what the error will be, use try/except for debugging purpose. Otherwise, you don't have to use try/except for every function.

IronPython - proper resources deallocation on sys.exit()

What is the best way to properly finalize python script when sys.exit() is called?
For example I have an app which:
- opened log file
- opened some USB gadget
- decide it's time to close the app
- call sys.exit(-1)
- (or alternatively it throw harsh exception - but I prefer first way as I was little piggy and some parts of code actually catch all exceptions, which would stop my termination exception...)
Then I would need some finalize() function which would be certainly called before exiting the interpreter. Finalize() would free USB gadget and close log file in exactly this order.
I tried def del but it is not called upon sys.exit and furthermore I can not decide in which order _del_s would be called.
Is there some salvation for me? Or do I have to do:
1. Top most try-catch-finally
2. Do the exit with some specific Exception
3. everywhere on each exception catch specify exactly what I'm catching?
See python's with statement.
class UsbWrapper(object):
def __enter__(self):
#do something like accessing usb_gadget (& acquire lock on it)
#usb_gadget_handle = open_usb_gadget("/dev/sdc")
#return usb_gadget_handle
def __exit__(self, type, value, traceback):
#exception handling goes here
#free the USB(lock) here
with UsbWrapper() as usb_device_handle:
usb_device_handle.write(data_to_write)
No matter whether the code throws exception or runs as desired, the USB lock is always released.
Ok I found the answer which suites me best:
import sys
try:
print "any code: allocate files, usb gadets etc "
try:
sys.exit(-1) # some severe error occure
except Exception as e:
print "sys.exit is not catched:"+str(e)
finally:
print "but all sub finallies are done"
print "shall not be executed when sys.exit called before"
finally:
print "Here we can properly free all resources in our preferable order"
print "(ie close log file at the end after closing all gadgets)"
as for recommended solution atexit - it would be nice and all but it does not work in my python 2.6. I tried this:
import sys
import atexit
def myFinal():
print "it doesn't print anything in my python 2.6 :("
atexit.register(myFinal)
print "any code"
sys.exit(-1) # is it pluged in?
print "any code - shall not be execute"
As for Wrapper solution - it's definitely most fancy - but honestly I can not say how it's better...
import sys
class mainCleanupWrapper(object):
def __enter__(self):
print "preallocate resources optionally"
def __exit__(self, type, value, traceback):
print "I release all resources in my order"
with mainCleanupWrapper() as whatsThisNameFor:
print "ok my unchaged code with any resources locking"
sys.exit(-1)
print "this code shall not be executed"
I found my solution - but frankly python seems to be getting pretty bulky and bloated...

Categories