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,
msg
won't be printed, asassertEqual
was technically never called for the offending iteration. - On the other hand, fundamentally,
foo(i) == i
failed 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?