It's generally accepted that using eval is bad practice. The accepted answer to this question states that there is almost always a better alternative. However, the timeit module in the standard library uses it, and I stumbled onto a case where I can't find a better alternative.
The unittest module has assertion functions of the form
self.assert*(..., msg=None)
allowing to assert something, optionally printing msg if it failed. This allows running code like
for i in range(1, 20):
self.assertEqual(foo(i), i, str(i) + ' failed')
Now consider the case where foo can raise an exception, e.g.,
def foo(i):
if i % 5 == 0:
raise ValueError()
return i
then
- On the one hand,
msgwon't be printed, asassertEqualwas technically never called for the offending iteration. - On the other hand, fundamentally,
foo(i) == ifailed to be true (admittedly becausefoo(i)never finished executing), and so this is a case where it would be useful to printmsg.
I would like a version that prints out msg even if the failure cause was an exception - this will allow to understand exactly which invocation failed. Using eval, I could do this by writing a version taking strings, such as the following (which is a somewhat simplified version just to illustrate the point):
def assertEqual(lhs, rhs, msg=None):
try:
lhs_val = eval(lhs)
rhs_val = eval(rhs)
if lhs_val != rhs_val:
raise ValueError()
except:
if msg is not None:
print msg
raise
and then using
for i in range(1, 20):
self.assertEqual('foo(i)', 'i', str(i) + ' failed')
Of course technically it's possible to do it completely differently, by placing each call to assert* within a try/except/finally, but I could only think of extremely verbose alternatives (that also required duplicating msg.)
Is the use of eval legitimate here, then, or is there a better alternative?