Smells: Stop using assert statements! 💩
I often come across code using the assert
python keyword to validate state. This is a mistake.
“A mistake?? Isn’t that the whole purpose of the keyword?" - you exclaim
You’re right! Which is why it’s insidious! Take the following statement.
assert invoice.paid
You’d think (and you’d be right) this statement will check the .paid
attribute of the invoice
object is “truthy” and would raise an AssertionError
otherwise.
I get the allure, it’s neat, it’s shorthand, it’s pythonic.
I feel there are three issues with it:
1. The assert
keyword can be trivially disabled!#
The assert
keyword will be straight up ignored if the Python interpreter is running with performance optimisations.
Imagine this scenario; a developer is tasked with improving runtime performance, and it seems adding the performance optimisation flag is a quick win right?
So they go ahead, and enable it, nothing errors, the program works, fab!
Except, not fab, your business logic which depends on asserts is now being ignored. Imagine the carnage! Payment issues, data integrity issues, sanity issues. This is BAD!
You may not become aware of this subtle issue for weeks, and finding the root cause will make hairlines recede. Will you ever be able to fix the data? Probably not!
“Oh no!" - you say
Fortunately there’s an easy way to prevent this - read on!
2. Truthyness is loose#
True
, 1
and "stonks"
are all truthy. If the invoice.paid
value was "stonks"
you’d probably like to be aware of that.
This won’t apply to every situation, but when it comes to important data, you better be damn sure it’s the right type of truthy.
You can do assert invoice.paid is True
of course, but devs love to shorthand.
3. AssertionError
is too generic#
No personal vendetta against AssertionError
but wouldn’t you rather have an InvoiceNotPaid
error? Or something actually descriptive?
Parsing the stack trace is far easier if the exception is descriptive. If you have assertions all over the codebase then AssertionError
is not helpful.
A better example#
class InvoiceNotPaid(Exception):
pass
def validate_invoice(invoice):
if invoice.paid is not True:
raise InvoiceNotPaid
But my codebase has lots of assert statements!#
I don’t suggest ripping them out! But you should add an exception to your code standards to avoid introducing more in the future.
For now, there’s a very simple prevention scheme. At the entrypoint to your application add the following code block as a function:
try:
assert False
raise Exception('Assertions have been disabled!')
except AssertionError:
pass
The block will raise an Exception
if the assertion passes, which will only happen if the assert
keyword is disabled. In practice this means the application will refuse to start, making it VERY clear something is wrong.
So when should I use assert
?#
Test cases are a better scenario for assert, and most of the test suites lean on it.
Fundamentally though the keyword is optional - and I personally prefer to avoid it.
Great, I learned something!#
If you found this useful, or you’d like to flame me 🔥, tweet me @juanjsebgarcia 😘