Python assert statement and code reusability

Go To StackoverFlow.com

2

The best practice seems to be to use assert for a condition that should never happen if the code is correct, and an exception for a condition that is a bit unusual but can happen (e.g., when memory runs out, or user input is invalid, or external connections are broken). I understand the rationale behind this practice as follows:

  1. assert will be disabled with -O interpreter flag. Conditions that may arise from external factors must not be allowed to be silently ignored, so assert there is inappropriate. OTOH, conditions that may only arise if my code is incorrect are hopefully eliminated through testing and debugging, so assert is fine.

  2. assert discourages the caller from handling the exception, since AssertionError is usually interpreted as "don't catch me, this is a catastrophic failure". Furthermore, it is too generic to catch. This is perfect when a bug is found; the typical handling for that would be to stop the execution and debug the code. It is not good if it's a common condition due to external reasons.

Suppose I write some code where I ensure that a certain function argument is always positive. If I find it to be negative, clearly I made a mistake in the code. Hence, I am going to assert that the argument is positive.

Later, someone finds this function useful in another application. They import it, and send all sorts of data to it. Now from the perspective of my function, receiving a negative value is actually quite likely; it is simply an invalid user input. Arguably, the assert is no longer appropriate, and should be replaced with an exception.

Since almost any code could potentially be reused one day, often without my knowledge, this argument seems to say "never use assert; only use exceptions". Obviously, this is not an accepted practice. What am I missing?

EDIT:

To be more specific, let's say the function cannot handle a negative argument at all. So once the argument is negative, the function will do one of the following:

  • raise an exception
  • fail an assert
  • continue execution, likely producing incorrect output

I can see how it would be nice if negative arguments were caught by the caller. But if the calls to the function are interspersed in dozens of places around the code, it's arguably detrimental to the code clarity due to the numerous repetitions of the same check. (Not to mention, it could be forgotten by accident.)

2012-04-04 20:50
by max
Nope, this argument says "Never use assert for checks regarding values from the outside". If you get the area of something from some internal helper function, it may be worth to assert area >= 0 (in the calling code, or next to the complicated symbolic math that does the calculation) - NoName 2012-04-04 21:01
@delnan Let's say I am testing an argument passed as an input to a function. The function is called dozens of times in my code. I wouldn't want to replace a single assert x > 0 at the beginning of the function with dozens of repetitions of same outside the function. Worse, another time the function is called someone will forget to add the assert - max 2012-04-04 21:17
BTW, here's a couple answers I read on the subject. http://stackoverflow.com/a/945135/336527. http://stackoverflow.com/a/3721183/336527. They all argue that a bug should result in an AssertionError, but didn't make it clear that it should be a bug in the scope where assert is placed. I suppose it was implied - max 2012-04-04 21:27


2

If the function you are writing/reusing is valid with positive or negative numbers, it is not the method that should contain the assert. The function calling the re-used function should have the assert because it is the function providing the invalid values to the function.

function x() {
   var i;
   // logic to set i. use assertion to test the logic.
   assert(i > 0);
   reusedFunc(i);
}

if reusedFunc(i) is not valid with negative numbers, it should throw an exception if passed a negative value.

2012-04-04 20:58
by Colin D
So you would use assert only to catch a bug in the code from the current scope or inner scope? I've never seen this clearly stated, but it sounds like a good approach. Let me think about it. BTW, I refer to Python, but it doesn't seem to matter in the example you gave - max 2012-04-04 21:26


1

assert statements are for things that you use while developing and debugging the code, not to guarantee critical API constraints.

For your positive number example, test the value and raise ValueError with a useful error message if using a negative value within your code would have bad consequences.

I tell people never to use assert statements. They are easy to write but yet they are so often inappropriate.

assert statements also fall over when people write long ones and decide to use parentheses to make the statement and message span multiple lines... This generates a SyntaxWarning in modern Python about the tuple that the author inadvertently created by using (condition, message) on a non-function statement but it hasn't always done so.

Another rule of thumb: If you ever see a unittest verifying that an AssertionError was raised, the code should not be using an assert.

If you don't use assert statements none of these things will ever bite you.

2012-04-06 01:41
by gps
Ads