Handling Exceptions in Arduino-Python Communications - python

I am trying to implement a system where outputs from an Arduino can be passed on to and printed in Python.
I have the following Arduino code snippet below:
void loop() {
if (Serial.available()) {
c = Serial.read();
if (c == '1') {
digitalWrite(pinLED1, HIGH);
digitalWrite(pinLED2, LOW);
//Serial.print("1 ON, 2 OFF\n");
}
My Python code snippet is as follow:
import serial
import time
arduino = serial.Serial('COM3', 9600, timeout=1)
msg = arduino.readline(arduino.inWaiting()) # read everything in the input buffer
time.sleep(3)
ASCIIdata = '121210'
for i in range(len(ASCIIdata)):
if ASCIIdata[i] == '1':
strin = '1'
arduino.write(strin.encode())
time.sleep(0.2)
#print(ASCIIdata[i])
try:
print ("Message from arduino: ")
print arduino.readline()
raise
except Exception as e:
print ("Fail to send!")
In the above example, I am trying to send inputs to Arduino via ASCIIdata, and when it matches the if-statement in Arduino, commands in the if-statement will be executed. I have purposely not do a print in Arduino and attempt to read something from Python by doing:
try:
print ("Message from arduino: ")
print arduino.readline()
raise
except:
print ("Fail to send!")
I observed that if I do not put raise in the try statement, the except statement will not be executed, but the program moves on after the specific timeout. Hence, I put raise in the try statement. May I ask if this is the correct approach to raising an exception?
I read that I should be doing except Exception as e instead of doing except because I would be catching all kinds of exception while the program is running. May I ask if this thought process is correct?

First of all, I'm not very much into Python, so I'm not really sure, but having experience in other languages I think that this answer is correct.
What you did is... wrong. You don't want to raise an exception every time.
Looking at the documentation of the readline function,
readlines() depends on having a timeout and interprets that as EOF
(end of file). It raises an exception if the port is not opened
correctly.
This means that a timeout does not raise exceptions. You can test if the buffer is empty:
try:
print ("Message from arduino: ")
buff = arduino.readline()
print buff
if not buff:
raise
except:
print ("Fail to send!")
Even if you may just skip the exception:
print ("Message from arduino: ")
buff = arduino.readline()
print buff
if not buff:
print ("Fail to send!")
If you prefer another behavior, write your own function (for instance this answer has something which looks like a reading function; you'll just have to add the exception raising.
As for the type of exception, usually it is best to use just one type of exception, in order to avoid catching everything. For instance:
class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
try:
raise MyError(2*2)
except MyError as e:
print 'My exception occurred, value:', e.value

To answer your first question, this is the correct way to catch exceptions. The caveat is that you need to know what exceptions the code inside the try block raises. In your case arduino.readline() will only raise an exception on a serial port issue. In other words you don't need to call raise yourself because that means it will always look like a failure.
Using except Exception as e allows you to handle specific exceptions that code in the try block might raise. This way you can respond in different ways.
For example you might have different response for different errors:
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise

Related

having problems with python error handlers

hello I am pretty new into python , but I am trying to handle errors, however when I try to ctrl + C or L I got input > ^Cerror which it should be only display error and not ^C or ^L how can I fix this ?
except KeyboardInterrupt:
print("error")
except Exception as e:
raise e
You can back up and overwrite it, but you can't "fix" it. That character comes from the keyboard echo of the symbiont terminal: your stdin processor is set to echo (print to screen) whatever is typed. That ^C couplet is printed by this I/O processing before your program gets to handle the exception. Thus, the \r option (or a sequence of backspaces, \b) is likely your best remedy.
import sys
try:
while True:
string = input("Show me something")
except KeyboardInterrupt:
print("\b\b error")
except Exception as e:
raise e
Output, with CTRL-C at keyboard:
Show me something error
If you want to wipe out the entire input line, use \r instead:
except KeyboardInterrupt:
print("\rerror", " "*80)
Output:
Show me something -- this line appears until I hit CTRL-C. Replaced by
error

Stop program from running after try / except python

I made a program that takes values from treeview and calculate something when button is pressed. I put try / except statement inside that function.
def SumAll():
try:
#do something (calculate)
except ValueError:
Error=messagebox.showinfo("Enter proper values")
pass
The problem is, program keeps running when messagebox.showinfo appears, and it gives the ValueError.
How can I fix that, and how can I put more than one error exception (IndexError, etc)?
You can re-raise the exception, and if the exception reaches the top of the stack, the program will exit
try:
#do something (calculate)
except ValueError:
Error=messagebox.showinfo("Enter proper values")
raise
Or you can manually call sys.exit
import sys
try:
#do something (calculate)
except ValueError:
Error=messagebox.showinfo("Enter proper values")
sys.exit(1)
To catch more than on in the same handler, you can do something like
try:
#do something (calculate)
except (IndexError, ValueError):
Error=messagebox.showinfo("Enter proper values")
raise
or if you want different handlers you can have
try:
#do something (calculate)
except IndexError:
Error=messagebox.showinfo("Some message")
raise
except ValueError:
Error=messagebox.showinfo("Enter proper values")
raise
You can catch multiples by:
try:
k = input().split()[2]
i = int(k[0])
except (IndexError, ValueError) as e:
print(e) # list index error: 'Hello World', Value error: 'Hello World Tata'
else:
print("no error")
this will guard against inputs that split to less then 3 items '5 a' as well as against int conversion errors: 'Hello World Is A Cool Thing' (`'Is'`` is not an integer).
Use How to debug small programs (#1) to debug through your real program, the error you get above this except is caught. You probably get a resulting error somewhere else because somethings not right with the return of your function.

Python: pseudo try before actually try and finding exception

I know I'm not clear on this thing but I couldn't be more specific in the title. Consider the following code:
try:
print "Try this out"
a = int("Blah blah")
except:
print "I got the exception"
The output of this code is-
Try this out
I got the exception
What I want the python to do is to check if it might raise an exception in the try: block first and then execute it. Else, just execute the except: block. Is it possible to do such a thing without nesting the multiple try-except blocks.
No, it's not possible, because you get an exception during execution. But you can do like this:
try:
a = int("Blah blah")
print "Try this out"
except:
print "I got the exception"
Your try statement prints up as no error happens in printing it ,but as soon as it tries to execute a, gets an error so except executes.
Try this way to catch the exception properly:
try:
a = int("Blah blah")
print ("Try this out")
except:
print ("I got the exception")

Python traceback.print_exc() returns 'None'

This function is supposed to catch exceptions in the main execution. If there is an exception it should print out the error with log.error(traceback.print_exc()) and clean up with exit_main().
def main():
try:
exec_app()
except KeyboardInterrupt:
log.error('Error: Backup aborted by user.')
exit_main()
except Exception:
log.error('Error: An Exception was thrown.')
log.error("-" * 60)
log.error(traceback.print_exc())
log.error("-" * 60)
exit_main()
Unfortunately log.error(traceback.print_exc()) does only return None if there is an exception. How can I make traceback print the full error report in this case?
PS: I use python 3.4.
From its __doc__:
Shorthand for 'print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)'
That is, it isn't supposed to return anything, its job is to print. If you want the traceback as a string to be logged, use traceback.format_exc() instead.
I usually use traceback.print_exc() just for debugging. In your case, to log your exception you can simply do the following:
try:
# Your code that might raise exceptions
except SomeSpecificException as e:
# Do something (log the exception, rollback, etc)
except Exception as e:
log.error(e) # or log(e.message) if you want to log only the message and not all the error stack

how to close python script without just activating "except" in try/except statement

I have a python script running over some 50,000 items in a database, it takes about 10 seconds per item and I want to stop it.
But, if i just press ctrl + C while the code is in the try except part, then my program enter the expect statement and delete that item then continue. The only way to close the program is to repeatedly delete items this way until I, just be sheer luck, catch it not in the try statement.
How do I exit the script without just killing a try statement?
EDIT 1
I have a statement that looks something like this:
for item in items:
try:
if is_item_gone(content) == 1:
data = (item['id'])
db.update('UPDATE items SET empty = 0 WHERE id = (%s);', data)
else:
do alot of stuff
except:
data = (item['id'])
db.delete('...')
EDIT 2
The top of my connect to db code looks like:
#!/usr/bin/python
import MySQLdb
import sys
class Database:
....
The issue is because you are using a blanket except which is always a bad idea, if you catch specific exceptions then when you KeyboardInterrupt your script will stop:
for item in items:
try:
if is_item_gone(content) == 1:
data = (item['id'])
db.update('UPDATE items SET empty = 0 WHERE id = (%s);', data)
else:
do alot of stuff
except MySQLdb.Error as e:
print(e)
data = (item['id'])
db.delete('...')
If you have other exceptions to catch you can use multiple in the except:
except (KeyError, MySQLdb.Error) as e
At the very least you could catch Exception as e and print the error.
for item in items:
try:
if is_item_gone(content) == 1:
data = (item['id'])
db.update('UPDATE items SET empty = 0 WHERE id = (%s);', data)
else:
do alot of stuff
except Exception as e:
print(e)
data = (item['id'])
db.delete('...')
The moral of the story is don't use a blanket except, catch what you expect and logging the errors might also be a good idea. The exception-hierarchy is also worth reading to see exactly what you are catching.
I would rewrite it like this:
try:
# ...
except KeyboardInterrupt:
sys.exit(1)
except Exception: # I would use a more specific exception here if you can.
data = (item['id'])
db.delete('...')
Or just
try:
# ...
except Exception: # I would use an even more specific exception here if you can.
data = (item['id'])
db.delete('...')
Python 2's exception hiearchy can be found here: https://docs.python.org/2/library/exceptions.html#exception-hierarchy
Also, many scripts are written with something like this boiler plate at the bottom:
def _main(args):
...
if __name__ == '__main__':
try:
sys.exit(_main(sys.argv[1:]))
except KeyboardInterrupt:
print >> sys.stderr, '%s: interrupted' % _SCRIPT_NAME
sys.exit(1)
When you use except: instead of except Exception: in your main body, you are not allowing this top level exception block to catch the KeyboardInterrupt. (You don't have to actually catch it for Ctrl-C to work -- this just makes it prettier when KeyboardInterrupt is raised.)
import signal
import MySQLdb
import sys
## Handle process signals:
def sig_handler(signal, frame):
global connection
global cursor
cursor.close()
connection.close()
exit(0)
## Register a signal to the handler:
signal.signal(signal.SIGINT, sig_handler)
class Database:
# Your class stuf here.
# Note that in sig_handler() i close the cursor and connection,
# perhaps your class has a .close call? If so use that.
for item in items:
try:
if is_item_gone(content) == 1:
data = (item['id'])
db.update('UPDATE items SET empty = 0 WHERE id = (%s);', data)
else:
do alot of stuff
except MySQLdb.Error as e:
data = (item['id'])
db.delete('...')
This would register and catch for instance Ctrl+c at any given time. The handler would terminate a socket accordingly, then exit with exit code 0 which is a good exit code.
This is a more "global" approach to catching keyboard interrupts.

Categories