A is for assert - Python A to Z
Code in this blog post was written with versions: Python: 3.14
Python A-Z is a blog series about Python. Each day, I share insights, ideas and examples for different parts of Python development that match with the letter of the day. Blaugust is an annual blogging festival in August where the goal is to write a blog post every day of the month.
The
assert statement
is a built-in Python statement that gives developers a nice short hand for
raising an AssertionError based on a
condition.
assert expression, "message"
An assert statement is syntactic shorthand for
if __debug__:
if not expression: raise AssertionError("message")
To see how it works in practice, let’s jump into a REPL session:
>>> age = 21
>>> assert age > 18, f"Needs to be over 18 years old, {age=}"
>>> age = 16
>>> assert age > 18, f"Needs to be over 18 years old, {age=}"
Traceback (most recent call last):
File "<python-input-5>", line 1, in <module>
assert age > 18, f"Needs to be over 18 years old, {age=}"
^^^^^^^^
AssertionError: Needs to be over 18 years old, age=16
When the expression is truthy, nothing happens. When it’s falsy however,
Python will raise an AssertionError and
if a message was provided, it will be printed after the error in the
traceback.
Writing good assertion messages will make your life easier in the future. When you write your code, you have a good idea of what the assertion tests but as time goes on, the memories will fade and if you run into a random AssertionError in your script (see use case 1 below), it may require a lot of mental work to get back on track.
Use case 1: failsafe for scripting
It’s handy for debugging but also as a sort of failsafe for scripts that you run manually.
When I write shell script style scripts with Python, I don’t always need to write the most robust code but a quick “make sure this is so” check that stops the execution is enough. In a use case like that, adding asserts is a quick way to make sure unexpected issues in data don’t make their way to wreck havoc later in the script.
When the script is run manually, it’s perfectly fine for it to crash to an unhandled exception. That’s not usually the case if you’re running it automatically (for example in CI or as a cronjob) so in those cases you want to make sure your script can either recover or properly logs its errors.
Let’s look at a simplified example:
#!/usr/bin/env -S uv run --quiet --script
import sys
path = sys.argv[1]
assert '..' not in path, f"Can't go up in directory hierarchy, {path=}"
# Run the script
(the shebang of the previous script makes it handy to write single-file executable Python scripts using uv)
Here, we have a script that accepts a file path as a command line argument and
does something with it. I wanted to make sure it doesn’t accidentally escape
to do things in folders above where it’s ran. This could happen accidentally
for example if it’s being run by another script that could have inserted an
.. into its list of filenames.
It’s not a perfect, robust error boundary as there could be files with double periods but it’s good enough for my script’s use case. The provided message lets me know if it skipped something because of double period.
NOTE: It’s important to note that assertions
will be disabled if the code is run with optimisations enabled (either by
running the script with python -O or
python -OO or having the environment
variable
PYTHONOPTIMIZE
set to a non-zero value). This means you cannot and should not rely on
asserts for data validation generally.
Use case 2: pytest
Popular testing library pytest uses asserts for test cases.
def test_empty_list_sums_to_zero():
assert sum([]) == 0
I find it much more elegant and readable than the custom assertion libraries
like unittest’s self.assertEqual. It
also allows writing tests without having to import anything in the test suite.
When you run this through pytest, it
will check whether sum([]) returns
0 or not and either passes or fails the
test.
If something above resonated with you, let's start a discussion about it! Email me at juhis@hamatti.org and share your thoughts. This year, I want to have more deeper discussions with people from around the world and I'd love if you'd be part of that.