Search code examples
pythonexceptionpython-3.xassertsoftware-design

Python assert statement and code reusability


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.)


Solution

  • 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.