I'm creating a calculator using PLY and I want to be able to stop the parsing when it encounters a division by 0.
If I raise an error, it will stop my program, which is not what I want.
I want to be able to print an error message and then ignore the rest of the parsed string.
Since I'm in a loop, it will end the parsing and ask again for a new input.
while True:
question = input('>>> ')
try:
answer = parser.parse(question)
if answer is not None:
print(answer)
except EOFError:
break
I have a class (not a parsing rule) for handling numbers and operations between them. If a division by zero occurs it will call the error method, which will just print an error message for now.
def error(self, op, other):
print('Illegal operation "{} {} {}" for {}'.format(self, op, other, type(self).__name__))
Which is fine if there is nothing else after the division, but will be a problem for this: 10/0 - 3 + 2 because the parsing is continuing and multiple errors will be thrown. I want to be able to stop the parsing after the very first error.
I was thinking something like lexer.skip(1) but for all the string, not just 1 token. Is there a similar method for this ?
Thanks a lot !
If you just want to interrupt the parse and discard the rest of the line being parsed, raise an exception (or let the Python divide by zero exception through). Here, I've created an exception class for calculator errors, so that bugs in the parser code will still result in backtraces. The code not shown is based on examples/calc/calc.py.
class CalcError(Exception):
def __init__(self, message):
self.message = message
# ...
def p_expression_div(p):
'''expression : expression '/' expression'''
if p[3] == 0:
raise CalcError("Divide by zero")
p[0] = p[1] / p[3]
Then you just need to modify your main loop a bit:
import readline
try:
while True:
question = input('>>> ')
try:
answer = parser.parse(question)
if answer is not None:
print(answer)
except CalcError as e:
print(e.message)
except EOFError:
print()
break
Related
I have 'main.py' and 'newcanmonitor.py'
I want to call up the return value "string" from newcanmonitor.py inside main.py
-----main.py----
import newcanmonitor
xy=newcanmonitor.read_bus('vcan0')
print(xy)
-----newcanmonitor.py------
def read_bus(bus_device):
"""Read data from `bus_device` until the next newline character."""
message = bus.recv(0.2)
while True:
if message:
break
message = bus.recv(0.2)
try:
string = "{}:ID={}:LEN={}".format("RX", message.arbitration_id, message.dlc)
for x in range(message.dlc):
string += ":{:02x}".format(message.data[x])
except Exception as e:
print(e)
return string
Obviously this is not working
Can you help me?
There is no reason that calling a function from another module wouldn't work.
You never use "bus_device" in your code. Could that be an omission and the source of your woes?
Let's say I have three functions that do different things but should react to a set of exceptions in the same way. One of them might look like:
def get_order_stat(self, Order_id):
status_returned = False
error_count = 0
while status_returned == False:
try:
stat_get = client.queryOrder(orderId=Order_id)
except MalformedRequest:
print('Order ID not yet findable, keep trying')
error_count += 1
time.sleep(int(1))
except InternalError:
print('Order check returned InternalError, keep trying')
error_count += 1
time.sleep(int(1))
except StatusUnknown:
print('Order check returned StatusUnknown, keep trying')
error_count += 1
time.sleep(int(1))
else:
status = stat_get['status']
status_returned = True
finally:
if error_count >= 10:
print('Error loop, give up')
break
return status
The vast majority of the code is the exception handling, and I'd like to avoid having to repeat it in every function that needs it. Is there a way to define something like an exception handling function containing the handling code? Ideally my function would end up effectively:
def get_order_stat(self, Order_id):
status_returned = False
while status_returned == False:
try:
stat_get = client.queryOrder(orderId=Order_id)
except:
handler_function()
else:
status = stat_get['status']
status_returned = True
return status
You practically already did it. Just define the handler_function() somewhere and it gets called when an Exception in the try block gets raised.
Maybe helpful: you can bind the Exception to a variable and use it for exception handling in the handler function:
except Exception as e:
handler_function(e)
Then you can for example do `print(e)̀ to give out the exception or do different handling for different exceptions in the function. Hope that helps!
You also can specify several exceptions in one line if you don't want to be general, but catch all specific exceptions with one statement:
except (ExceptionType1, ExceptionType2, ExceptionType3) as e:
handler_function(e)
I might write a decorator function for the exception handling; for instance using functool.wraps.
from functool import wraps
def retry(f):
#wraps(f)
def wrapper(*args, **kwargs):
error_count = 0
while error_count < 10:
try:
return f(*args, **kwargs)
except MalformedRequest:
print('Order ID not yet findable, keep trying')
except InternalError:
print('Order check returned InternalError, keep trying')
error_count += 1
time.sleep(int(1))
print('Error loop, give up')
return None
return wrapper
Then you can write a very simple API call function, and wrap it with the retry wrapper:
#retry
def get_order(order_id):
stat_get = client.queryOrder(orderId=order_id)
return stat_get['status']
In your original function, notice that you can move the contents of the try...else block into the main try block without affecting the logic (extracting the value from the query result won't raise one of the network-related exceptions), and then you can just return out of the try block instead of arranging to stop the loop. Then the contents of the try block are what I've broken out here into get_order(). I restructured the remaining loop a little and turned it into decorator form.
Below is my code in which my while loop is calling my function (function_called) to process the log file. When there is an exception when my function is processing each line then it should ignore rest of the code and transfer the control back to while loop to go to the next line.
So it should be:
--Fetch Line--
--If index error exception in line--
--ignore code--
--move to the next line
My code overview is mentioned below
def function_called():
try:
--Something is happening here--
except IndexError:
--If there is an exception then code needs to be ignored
and it should be transfered to the while loop to go to the next line--
f = open('/data/qantasflight/run/tomcat/logs/localhost_access_log.2016-03-31.txt', 'r')
while True:
line = ''
while len(line) == 0 or line[-1] != '\n':
tail = f.readline()
if tail == '':
continue
line = tail
print (line, end='')
# print (line)
sleep(0.1)
function_called(line)
You should catch the exception at the place where you want to handle it, i.e. in the while loop.
def function_called():
""" something, which might raise an index error """
while True:
""" do stuff """
try:
function_called(line)
except IndexError:
continue
If you want to do more explicit error handling in function_called, you can do that and just raise the exception again:
def function_called():
try:
""" something, which might raise an index error """
except IndexError:
""" more error handling """
raise
Check out the chapter about error handling in the official documentation.
I'm trying to extract data from an xml file. A sample of my code is as follows:
from xml.dom import minidom
dom = minidom.parse("algorithms.xml")
...
parameter = dom.getElementsByTagName("Parameters")[0]
# loop over parameters
try:
while True:
parameter_id = parameter.getElementsByTagName("Parameter")[m].getAttribute("Id")
parameter_name = parameter.getElementsByTagName("Name")[m].lastChild.data
...
parameter_default = parameter.getElementsByTagName("Default")[m].lastChild.data
print parameter_id
print parameter_default
m = m+1
except IndexError:
#reached end of available parameters
pass
#except AttributeError:
#parameter doesn't exist
#?
If all elements for each parameter exist, the code runs correctly. Unfortunately the data I am supplied often has missing entries in it, raising an AttributeError exception. If I simply pass on that error, then any elements that do exist but are retrieved later in the loop than when the exception occurred are skipped, which I don't want. I need some way to continue where the code left off and skip to the next line of code if this specific exception is raised.
The only way to work around this that I can think of would be to override the minidom's class methods and catch the exception there, but that seems far too messy and too much work to handle what should be a very simple and common problem. Is there some easier way to handle this that I am missing?
Instead of "an individual try-except block for every statement", why not abstract out that part?
def getParam(p, tagName, index, post=None):
post = post or lambda i: i
try:
return post(p.getElementsByTagName(tagname)[index])
except AttributeError:
print "informative message"
return None # will happen anyway, but why not be explicit?
then in the loop you could have things like:
parameter_id = getParam(parameter, "Parameter", m, lambda x: x.getAttribute("Id"))
parameter_name = getParam(parameter, "Name", m, lambda x: x.lastChild.data)
...
I think there are two parts to your question. First, you want the loop to continue after the first AttributeError. This you do by moving the try and except into the loop.
Something like this:
try:
while True:
try:
parameter_id = parameter.getElementsByTagName("Parameter")[m].getAttribute("Id")
parameter_name = parameter.getElementsByTagName("Name")[m].lastChild.data
...
parameter_default = parameter.getElementsByTagName("Default")[m].lastChild.data
print parameter_id
print parameter_default
m = m+1
except AttributeError:
print "parameter doesn't exist"
#?
except IndexError:
#reached end of available parameters
pass
The second part is more tricky. But it is nicely solved by the other answer.
The runtime keeps telling me:
expected an indented block
But I don't want write nothing inside my except block, I just want it to catch and swallow the exception.
Just write
pass
as in
try:
# Do something illegal.
...
except:
# Pretend nothing happened.
pass
EDIT: #swillden brings up a good point, viz., this is a terrible idea in general. You should, at the least, say
except TypeError, DivideByZeroError:
or whatever kinds of errors you want to handle. Otherwise you can mask bigger problems.
For those who are very unclear as to why you would want to do this. Here is an example where I initially thought that an empty block would be a good idea:
def set_debug_dir(self, debug_dir=None):
if debug_dir is None:
debug_dir = self.__debug_dir
elif isinstance(debug_dir, (Path, str)):
debug_dir = debug_dir # this is my null operation
elif isinstance(debug_dir, list):
debug_dir = functools.reduce(os.path.join, debug_dir)
else:
raise TypeError('Unexpected type for debug_dir: {}'.format(type(debug_dir).__name__))
But it would be more clear to reorganize the statement:
def set_debug_dir(self, debug_dir=None):
if debug_dir is None:
debug_dir = self.__debug_dir
elif isinstance(debug_dir, list):
debug_dir = functools.reduce(os.path.join, debug_dir)
elif not isinstance(debug_dir, (Path, str)):
raise TypeError('Unexpected type for debug_dir: {}'.format(type(debug_dir).__name__))
I've never done this in more permanent code, but I frequently do it as a placeholder
if some_expression:
True
else:
do_something(blah)
Just sticking a True in there will stop the error. Not sure if there's anything bad about this.