I have a function that will write to stderr when an error occurs:
class Scanner:
...
def next(self):
if ...
...
if not open_comment_count == closed_comment_count:
...
sys.stderr.write("error: unclosed comment at position ({0}, {1})".format(start_position, end_position))
...
break
...
else:
sys.stderr.write("error: illegal character \'{0}\' found " \
"at position #({1}, {2})".format(self.input_text[self.curr_position],
self.curr_position, self.curr_position))
error_flag = 1
break
if error_flag == 0:
token = Token(kind=4, eof_value="eof", start_position=self.curr_position, end_position=self.curr_position)
return token
the function is very long so I included the parts where I write to stderr. What I would like to do is when those errors occur, I want to print those errors and terminate the program. So in my main, I would like to have:
def main():
s = Scanner(input_string)
try:
while(True):
s.next()
except:
# print error message and stop
What i am stuck on is what should the exception be. I want to somehow catch that something was written to stderr and I want that caught and printed out in main and then the program to stop. Basically running until an exception occurs. I know this can be done by writing my own exception, but is it possible to just check if something was written to stderr and then print it out?
What you want to do mixes up concerns: reporting an error becomes a signal that there has been an error. You could do that by creating a wrapper for stderr that sets some kind of flag:
import sys
class MyStdErr(object):
stderr = sys.stderr
waserr = False
def write(self, text):
self.waserr = True
self.stderr.write(text)
sys.stderr = MyStdErr()
Now sys.stderr.waserr is True if stderr was written to at any point.
I think raising an exception containing the error message, catching it, and printing the error (as shown by Nathan) is considerably better, however.
Instead of writing to stderr is there a reason you cant just raise errors with what you're writing to stderr as the message? Then you can just catch whatever error you decide to raise. Something like
class Scanner:
...
def next(self):
if ...
...
if not open_comment_count == closed_comment_count:
...
raise InputError("error: unclosed comment at position ({0}, {1})".format(start_position, end_position))
...
break
...
else:
raise InputError("error: illegal character \'{0}\' found " \
"at position #({1}, {2})".format(self.input_text[self.curr_position],
self.curr_position, self.curr_position))
error_flag = 1
break
if error_flag == 0:
token = Token(kind=4, eof_value="eof", start_position=self.curr_position, end_position=self.curr_position)
return token
and then
def main():
s = Scanner(input_string)
try:
while(True):
s.next()
except InputError as e:
print e
EDIT
to address your comment, yes. And if you want do do it with a custom error name you can do
class MyException(Exception):
pass
and then raise MyException('message')
If you want to cause an exception when you write to stderr, then close stderr. Now when some function writes to stderr, I guess it will get an IOError. Getting the error message will be harder.
You could catch all exceptions and print out the exception, but this still won't capture the message to stderr.
Related
can someone help me with terminating this program from if statement. I can't get it done. I've tried to this with sys.quit, but it seems to be not suitable for try/except block and I can't break out of the loop within thread. I could do this in the run() method, but it's a little bit useless to build a thread and then try to do something outside of it. It feels like there is something wrong about it. Here's code:
class TradingBot:
def init(self) -> None:
self.api = tradeapi.REST(key_id=API_KEY, secret_key=SECRET_KEY, base_url=BASE_URL, api_version='v2')
def simple_thread(self):
try:
account = self.api.get_account()
clock = self.api.get_clock()
balance_change = float(account.equity) - float(account.last_equity)
condition_1 = balance_change > 0
condition_2 = balance_change < 0
if condition_1:
pass
#Figure out something to quit if condition 1 is met
elif condition_2:
pass
#Figure out something to quit if condition 2 is met
except:
print('Some error has occured')
def run(self):
while True:
execute = threading.Thread(target=self.simple_thread())
execute.start()
time.sleep(1)
sys.exit attempts to exit by throwing a SystemExit exception, which is caught by the bare except clause. I'm not sure why you need the try/except at all, but you should probably at least change it to catch only Exception, which, unlike BaseException, only includes normal exceptions, not special ones such as SystemExit.
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
I have set of Python scripts which are calling functions in a nested way. For each of these functions I have a try, except statement to capture every exception and print them. I would like to send an e-mail alert containing the complete sequence of exceptions encountered during the execution. Example:
import sys
def SendAlert(ErrorMessage):
try:
#send email alert with error message
#[...]
except:
print(str(sys.exc_info()))
return(sys.exc_info())
def ParentFunction():
try:
#call ChildFunction
ChildResult = ChildFunction()
#do stuff with ChildResult
#[...]
return ParentResult
except:
ErrorMessage = str(sys.exc_info())
print(ErrorMessage)
SendAlert(ErrorMessage)
def ChildFunction():
try:
#do stuff
#[...]
return ChildResult
except:
print(str(sys.exc_info()))
return(sys.exc_info())
#main
if __name__ == '__main__':
Result = ParentFunction()
The code above would behave as follow in case of error in ChildFunction which is the most nested function:
ChildFunction encounters an exception it will print it and return
the error message to ParentFunction
ParentFunction will fail because ChildResult contains an error message and not a valid value
ParentFunction will trigger and exception and send its own error message in the e-mail alert
In addition to the error message from ParentFunction, I would like the e-mail alert to contain the error message from ChildFunction. Note that I would like to avoid passing ChildResult variable to SendAlert function in the except statement of ParentFunction because in real life my program has a lot of nested functions and it would mean passing the result variable of every single function into the except statement.
How would you achieve this? Is there a way to access the complete sequence of errors triggered by the whole program?
Thanks
you don't need to return exceptions obtained with sys.exc_info: we can just re-raise it
try:
# do stuff
# FIXME: we should avoid catching too broad exception
except Exception as err:
# do stuff with exception
raise err
so your example may look like
def SendAlert(ErrorMessage):
try:
# send email alert with error message
# [...]
pass
# what kind of exceptions may occur while sending email?
except Exception as err:
print(err)
raise err
def ParentFunction():
try:
# call ChildFunction
ChildResult = ChildFunction()
ParentResult = ChildResult
# do stuff with ChildResult
# [...]
return ParentResult
# FIXME: we should avoid catching too broad exception
except Exception as err:
ErrorMessage = str(err)
# why do we need to print again?
print(ErrorMessage)
SendAlert(ErrorMessage)
def ChildFunction():
try:
ChildResult = 0
# do stuff
# [...]
# let's raise `ZeroDivisionError`
ChildResult /= 0
return ChildResult
# FIXME: we should avoid catching too broad exception
except Exception as err:
print(err)
raise err
# main
if __name__ == '__main__':
Result = ParentFunction()
Further improvements
For printing full error traceback we can use logging module like
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def SendAlert(ErrorMessage):
try:
# send email alert with error message
# [...]
pass
# what kind of exceptions may occur while sending email?
except Exception as err:
logger.exception('Error while sending email')
# we're not receiving values from this function
raise err
def ParentFunction():
try:
# call ChildFunction
ChildResult = ChildFunction()
ParentResult = ChildResult
# do stuff with ChildResult
# [...]
return ParentResult
# FIXME: we should avoid catching too broad exception
except Exception as err:
# this will log full error traceback
# including `ChildFunction`
logger.exception('Error in ParentFunction')
ErrorMessage = str(err)
SendAlert(ErrorMessage)
def ChildFunction():
ChildResult = 0
# do stuff
# [...]
# e. g. let's raise `ZeroDivisionError`
ChildResult /= 0
return ChildResult
# main
if __name__ == '__main__':
Result = ParentFunction()
And it is just the tip of the iceberg, logging is awesome and you definitely should use it.
Further reading
Logging HOWTO,
SMTPHandler for sending errors on email via SMTP
You can, also, create a custom exception that can take a descriptive error message and return it.
Here is, a trivial example that you can modify and implement it into your code untill it fills your needs:
class MyCustomError(Exception):
def __init__(self, err):
Exception.__init__(self)
self.error = err
def __str__(self):
return "%r" % self.error
a = 1
try:
if a != 0:
raise MyCustomError("This is an Error!")
except MyCustomError as err:
print(err)
Output:
'This is an Error!'
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.
My tkinter app has 2 threads (I need them) and I found on stackoverflow a wonderful function tkloop(), which is made for tkinter-only-one-main-thread; it uses Queue. It does show tkMessagebox when I do this:
self.q.put((tkMessageBox.askyesno,("Cannot download it", "Download \"" + tag +"\" via internet site"),{}, self.q1 ))
But when I made my own function, it somehow doesn't execute the function
self.q.put((self.topleveldo,(resultlist),{},None))
There's only one class App:
self.q=Queue()
def tkloop(self):
try:
while True:
f, a, k, qr = self.q.get_nowait()
print f
r = f(*a,**k)
if qr: qr.put(r)
except:
pass
self.okno.after(100, self.tkloop)
def topleveldo(resultlist):
print ("executed - actually this never prints")
self.choice=Toplevel()
self.choices=Listbox(self.choice)
for result in resultlist:
self.choices.insert(END,str(result))
choosebutton=Button(text="Vybrat",command=self.readchoice)
def readchoice(self):
choice=int(self.choices.curselection())
self.choice.destroy()
self.q1.put(choice)
another code in a method in class App, run by the second thread:
def method(self):
self.q1=Queue()
self.q.put((self.topleveldo,(resultlist),{},None))
print ("it still prints this, but then it waits forever for q1.get, because self.topleveldo is never executed")
choice=self.q1.get()
Log errors in the tkloop exception handler - right now you don't know if the call to topleveldo failed (it probably did). The problem is that (1) (resultlist) is just resultlist, not a tuple with 1 argument like topleveldo expects. And (2) tkloop only puts a response if the 4th parameter in the message is a queue. You can fix it with:
self.q.put((self.topleveldo,(resultlist,),{},self.q1))
Added:
tkloop should always return a message, even if it caught an exception, so that callers can reliably call q.get() to get a response. One way to do this is to return the exception that the called program raised:
def tkloop(self):
while True:
try:
f, a, k, qr = self.q.get_nowait()
print f
r = f(*a,**k)
if qr:
qr.put(r)
del f,a,k,qr
except Exception, e:
if qr:
try:
qr.put(e)
except:
# log it
pass
self.okno.after(100, self.tkloop)