Skip to content Skip to sidebar Skip to footer

Python Assertion Style

I'm wondering if what I'm doing is an appropriate method of assertions. I'm trying to making something both concise and very correct for Python's style guides. try: assert self

Solution 1:

The assert statement should only be used to check the internal logic of a program, never to check user input or the environment. Quoting from the last two paragraphs at http://wiki.python.org/moin/UsingAssertionsEffectively ...

Assertions should not be used to test for failure cases that can occur because of bad user input or operating system/environment failures, such as a file not being found. Instead, you should raise an exception, or print an error message, or whatever is appropriate. One important reason why assertions should only be used for self-tests of the program is that assertions can be disabled at compile time.

If Python is started with the -O option, then assertions will be stripped out and not evaluated. So if code uses assertions heavily, but is performance-critical, then there is a system for turning them off in release builds. (But don't do this unless it's really necessary. It's been scientifically proven that some bugs only show up when a customer uses the machine and we want assertions to help there too. )

With this in mind, there is virtually never a reason to catch an assertion in user code, ever, as the entire point of the assertion failing is to notify the programmer as soon as possible that there is a logic error in the program.

Solution 2:

I prefer to not catch the assertion within the function, and instead make sure the caller handles any errors. This also allows the caller to examine any unhandled errors and examine the traceback to see exactly what went wrong.

You can also add an error message to your assertion statements.

assert x > 0, "x must be greater than 0"

Solution 3:

If the calling function expects input of 0 on success and -1 on failure, I would write:

defprepare_for_connection(*args, **kwargs):
    if (self.handle isnotNone):
        return -1ifnot (isinstance(port_number, int) orisinstance(port_number, float)): 
        return -1if port_number < 0:
        return -1# function bodyreturn0

Invoking the mechanism of throwing and catching assertion errors for non-exceptional behavior is too much overhead. Assertions are better for cases where the statement should always be true, but if it isn't due to some bug you generate an error loudly in that spot or at best handle it (with a default value) in that spot. You could combine the multiple-if conditionals into one giant conditional statement if you prefer; personally I see this as more readable. Also, the python style is to compare to None using is and is not rather than == and !=.

Python should be able to optimize away assertions once the program leaves the debugging phase. See http://wiki.python.org/moin/UsingAssertionsEffectively

Granted this C-style convention of returning a error number (-1 / 0) from a function isn't particularly pythonic. I would replace -1 with False and 0 with True and give it a semantically meaningful name; e.g., call it connection_prepared = prepare_for_connection(*args,**kwargs), so connection_prepared would be True or False and the code would be very readable.

connection_prepared = prepare_for_connection(*args,**kwargs)
if connection_prepared:
    do_something()
else:
    do_something_else()

Solution 4:

return -1

Python has a different method of handling errors than C. If there is a problem with the data provided, just let the AssertionError pass through, or raise a TypeError or ValueError with a custom error message. A custom error message with an assert statement is the easiest:

assert port_number > 0, "Invalid port number"

The fact that assert statements can be disabled at compile time might be a reason to re-think if you want to use assert statements in your situation. Common practice is not to use assert statements for validating input from users of your functions, and just for internal sanity checks. On the other hand, the borders between sanity checks and validation are not well-defined. Examples for parts of your code without assert statements:

if port_number <= 0:
    raise ValueError('Invalid port number')if not isinstance(port_number, (int, float)):
    raise TypeError('Port number must be some kind of number')

Personally i use assert statements for validating data which, if invalid, would lead to a crash sooner or later anyway (see "duck-typing"). I also use assert statements heavily during development, to sanity-check my data like it would've been in a statically typed language. I only use these kinds of assertions if i strongly doubt the stability and reliability of my own code.

Next line:

assert self.handle == None

If i recall correctly, PEP8 says you should write assert self.handle is None. At least it's endorsed by people who are smarter than me.

assertisinstance(port_number, int) or isinstance(port_number, float)

If you really need this, it could be written as isinstance(port_number, (int, float)). But it turns out you don't. You shouldn't care if somebody passes a numerical primitive type or some self-made class that overloads all the comparison operators.

Maybe one thing you could do is to try to cast the port to an integer and see if it's usable or not:

try:
    port_number = int(port_number)
except ValueError:
    raise ValueError("Invalid port number")

And also in this case you could just let the ValueError pass through, the message would be less informative to a novice though.

Post a Comment for "Python Assertion Style"