How to write an empty indentation block in Python? - python

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.

Related

Python - Ply Parser : How can I stop the parsing wherever I want?

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

How to improve code clarity in nested try-except-else clauses?

Sometimes I have a cascade of different things I can try to accomplish a task, e. g. If I need to get a record I can first try to find the record, and if this fails, I can create the missing record, and if this also fails, I can use a tape instead.
Failing is represented by throwing an exception my code needs to catch.
In Python this looks something like this:
try:
record = find_record()
except NoSuchRecord:
try:
record = create_record()
except CreateFailed:
record = tape
This already has the disadvantage of piling-up indentations. If I have five options, this code won't look good.
But I find it even more problematic when there are also else clauses to the try-except clauses:
try:
record = find_record()
except NoSuchRecord:
try:
record = create_record()
except CreateFailed:
record = tape
logger.info("Using a tape now")
else:
logger.info("Created a new record")
else:
logger.info("Record found")
The find_record() and the corresponding Record found message are as far apart as possible which makes it hard to read code. (Moving the code of the else clause directly into the try clause is only an option if this code is definitely not raising one of the exceptions caught in the except statement, so this is no general solution.)
Again, this ugliness gets worse with each added level of nesting.
Is there a nicer way to put this into Python code
without changing the behavior and
while keeping the try and the except clauses of one topic closely together and/or
maybe also avoiding the piling-up nesting and indenting?
You can use a for loop to successively try variants:
for task, error in ((find_record, NoSuchRecord), (create_record, CreateFailed)):
try:
result = task()
except error:
continue
else:
break
else:
# for..else is only entered if there was no break
result = tape
If you need an else clause, you can provide it as a separate function:
for task, error, success in (
(find_record, NoSuchRecord, lambda: logger.info("Record found")),
(create_record, CreateFailed, lambda: logger.info("Created a new record"))
):
try:
result = task()
except error:
continue
else:
success()
break
else:
result = tape
logger.info("Using a tape now")
Take note that the default case tape is not part of the variants - this is because it has no failure condition. If it should execute with the variants, it can be added as (lambda: tape, (), lambda: None).
You can put this all into a function for reuse:
def try_all(*cases):
for task, error, success in cases:
try:
result = task()
except error:
continue
else:
success()
return result
try_all(
(find_record, NoSuchRecord, lambda: logger.info("Record found")),
(create_record, CreateFailed, lambda: logger.info("Created a new record")),
(lambda: tape, (), lambda: logger.info("Using a tape now")),
)
In case the tuples seem difficult to read, a NamedTuple can be used to name the elements. This can be mixed with plain tuples:
from typing import NamedTuple, Callable, Union, Tuple
from functools import partial
class Case(NamedTuple):
task: Callable
error: Union[BaseException, Tuple[BaseException, ...]]
success: Callable
try_all(
Case(
task=find_record,
error=NoSuchRecord,
success=partial(logger.info, "Record found")),
(
create_record, CreateFailed,
partial(logger.info, "Created a new record")),
Case(
task=lambda: tape,
error=(),
success=partial(logger.info, "Using a tape now")),
)
You could break that into multiple functions ?
def handle_missing():
try:
record = create_record()
except CreateFailed:
record = tape
logger.info("Using a tape now")
else:
logger.info("Created a new record")
return record
def get_record():
try:
record = find_record()
except NoSuchRecord:
record = handle_missing()
else:
logger.info("Record found")
return record
And then you'd use it like,
record = get_record()
I think following code is more readable and clean. Also I am sure in real problem we need some parameters to be sent to "find_record" and "create_record" functions like id, some, values to create new record. The factory solution need those parameters also be listed in tuple
def try_create(else_return):
try:
record = create_record()
except CreateFailed:
record = else_return
logger.info("Using a tape now")
else:
logger.info("Created a new record")
def try_find(else_call= try_create, **kwargs):
try:
record = find_record()
except NoSuchRecord:
try_create(**kwargs)
else:
logger.info("Record found")
try_find(else_call=try_create, else_return=tape)

How to best remove redundant calls from a code block, that should be executed when the block exists successfully?

I'm trying to simplify the following code (removing the redundant prints), but can't find a satisfying way to do this:
original code
def main():
if expression1:
print("1")
print("always_do_this")
return
if expression2:
print("2")
print("always_do_this")
return
# ... possibly more expressions and thus redundancy
print("always_do_this")
# do something else
My first idea was a try-(except-)else combination, but the else is not executed on a return in the try-block.
Solution 1 - extracting into a separate function
def func():
if expression1:
print("1")
return True
if expression2:
print("2")
return True
return False
def main():
result = func()
print("always_do_this")
if result:
return
# do something else
Solution 2 - workaround using finally
def main():
error = False
try:
if expression1:
print("1")
return
if expression2:
print("2")
return
except:
error = True
raise
finally:
if not error:
print("always_do_this")
# do something else
Surely there must be a better way to achieve this in python?
PS: Also any ideas for a better title would be appreciated...
PPS: I'm not directly asking about (subjective) codestyle, but wondering if there is a way to write this that I didn't consider (e.g. a language construct/pattern, that also makes the code more concise/cleaner; obviously there are a lot worse ways).
Check if your flow did not enter the first two if blocks by checking for the opposite of the first two if statements joined by an andso that you can execute "do something else" only if the first two if statements failed. Return at the end instead of in the middle of the if statements.
def main():
expression1 = True
expression2 = False
if expression1:
print("1")
elif expression2:
print("2")
print("always_do_this")
if not expression1 and not expression2:
# do something else
return
If the thing you always want to do is closing a file, I would use a with statement. In a more general case you can create your own context manager to have full control of what gets run at the end.
Sample code:
class my_closer:
def __enter__(self):
return True
def __exit__(self, type, value, traceback):
if type is None:
print("always_do_this")
else
print("An exception was raised: {}".format(type))
def main():
with my_closer() as c:
if someexpr:
print("1")
return
if someexpr:
print("2")
return
I added a superfluous else to print something about the exception in case of error, but leave it out to reproduce your original code more accurately.
This code is not shorter than yours with the trivial print statement, but I like this method for more complex "closer" code.
You can also define the context manager this way using the contextlib library:
from contextlib import contextmanager
#contextmanager
def my_closer(*args, **kwds):
try:
yield True
except:
#print("some error happened")
raise
else:
print("always_do_this")
References:
http://effbot.org/zone/python-with-statement.htm
https://docs.python.org/3/library/stdtypes.html#typecontextmanager
https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager

How to use For Else if you want the for loop to always finish?

I was looking at this SO question about the Python For Else flow control and I thought I had a portion of code that which is very close to the example given. I think that my code is very close to the example code except that I want the for loop to finish (for logging purposes).
for module_name, class_name in BASIC_PARSERS_TO_RUN:
full_module_name = "parsers." + module_name
parser = getattr(import_module(full_module_name), class_name)(
logger=logger)
parser_data = parser.parse(cluster_path)
if parser_data is None or parser_data == "0":
# Basic None instead of "None" so that json is serialized to null
# instead of "None"
json_data_list.append({module_name: parser_data})
failed_basic_checks = True
else:
json_data_list.append({module_name: str(parser_data)})
# Checking if we have a valid data set.
if failed_basic_checks:
json_data_list.append({"basic_validation_succeeded": False})
return json.dumps(json_data_list)
# We've run into a dataset which isn't valid.
exit(1)
Is there any way to change my for loop to use the for else flow control?
found_obj = None
for obj in objects:
if obj.key == search_key:
found_obj = obj
break
else:
print 'No object found.'
The code as written is just fine; there's no reason to use a for/else structure.
According to the docs, an else after a loop is always executed unless the loop was terminated by a break statement. So if you are not using break statements in the loop, an else clause is unnecessary; you should simply put the relevant code after the loop.

Python - Continuing after exception at the point of exception

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.

Categories

Resources