assert

A one-line sanity check for things that must be true if your code is correct. It is a statement, not a function — parentheses around both parts turn it into a check that can never fail.

Errors & contextPython 3 (all)Live demo
assert
assert condition
with a message
assert condition, 'message'
means (roughly)
if __debug__:
    if not condition:
        raise AssertionError(msg)
Use for
assert invariants and assumptions in your own code and in tests
Result
a statement — nothing if the condition is truthy, AssertionError(message) if not
Pairs with
AssertionError, __debug__, pytest, isinstance()
Watch out
never assert (cond, msg) — a tuple is always true; -O removes every assert

Demo

Live evaluation
An internal invariant: take() assumes callers never ask for more than is in stock.
Try:
Inputs
nintmore than 5 fails
Code
def take(stock, n):
    assert n <= stock, f'cannot take {n}, only {stock} left'
    return stock - n
take(5, 3)
Result
2

In the tuple trap tab, the first assert passes for every x: (x > 0, "...") is a two-item tuple, and non-empty tuples are truthy. CPython notices at compile time and prints "SyntaxWarning: assertion is always true, perhaps remove parentheses?" to stderr — easy to miss in a log, invisible here. The second form is the real check. The failed-assert message is exactly the value after the comma.

Syntax slots

NameTypeRequiredDescription
conditionexpressionyesTested for truthiness. Falsy → AssertionError.
messageexpressionnoEvaluated only when the assert fails, and passed to AssertionError. Any object; usually an f-string with the offending values.

Common patterns

Invariant inside your own code
State what must be true if the code is correct.
assert len(result) == len(items), 'lost items while merging'
Test assertion
pytest rewrites plain asserts to show both sides of a failed comparison.
def test_total():
    assert total([1, 2]) == 3
Narrowing a type
Documents an assumption (and satisfies type checkers).
assert user is not None
print(user.name)
Long condition over several lines
Parenthesize only the condition, never the condition and message together.
assert (
    start <= end
), f'bad range {start}..{end}'

Examples

1. A failing assert with a message
assert 1 > 2, 'math is broken'
Returns
AssertionError: math is broken
2. Without a message
assert []
Returns
AssertionError
3. The message is any object
assert 0, {'code': 42}
Returns
AssertionError: {'code': 42}
4. The message is evaluated only on failure
def details(): print('building message') return 'details' assert True, details() assert False, details()
Returns
building message AssertionError: details
5. The tuple warning, captured
import warnings with warnings.catch_warnings(record=True) as caught: warnings.simplefilter('always') compile("assert (1 > 2, 'msg')", '<demo>', 'exec') print(caught[0].category.__name__, caught[0].message)
Returns
SyntaxWarning assertion is always true, perhaps remove parentheses?
6. Under -O, asserts are gone
code = compile("assert False, 'boom'", '<demo>', 'exec', optimize=1) exec(code) print('no error under -O')
Returns
no error under -O
7. __debug__ is what -O switches off
__debug__
Returns
True

Pitfalls

1. assert (condition, message) never fails
assert is not a function. The parentheses build a tuple, and a non-empty tuple is always true — the only sign is a SyntaxWarning on stderr.
parenthesized pair
x = -1
assert (x > 0, 'x must be positive')
'passed'
'passed'
comma outside
x = -1
assert x > 0, 'x must be positive'
'passed'
AssertionError: x must be positive
2. Checks that must run in production
python -O (or PYTHONOPTIMIZE) compiles asserts away. Here compile(optimize=1) does the same: the overdraft goes through. Use if + raise for anything that guards real data.
assert as a guard
src = '''
def withdraw(balance, amount):
    assert amount <= balance, 'insufficient funds'
    return balance - amount
print(withdraw(10, 50))
'''
exec(compile(src, '<demo>', 'exec', optimize=1))
-40
if + raise
src = '''
def withdraw(balance, amount):
    if amount > balance:
        raise ValueError('insufficient funds')
    return balance - amount
print(withdraw(10, 50))
'''
exec(compile(src, '<demo>', 'exec', optimize=1))
ValueError: insufficient funds

When to use

Use it
  • Invariants: things that are true unless your own code has a bug
  • Tests (pytest is built on plain assert)
  • Documenting an assumption at the top of a tricky block
Reach for something else
  • Validating user input, file contents or API arguments → if … raise ValueError / TypeError
  • Anything with side effects in the condition — it disappears under -O
  • Security or permission checks → an explicit if + raise

Notes

CPython impl
With -O the compiler emits no code at all for assert statements, and __debug__ is False. The check happens at compile time, so .pyc files for optimized runs are cached separately (.opt-1.pyc)
Message
On failure the message expression becomes AssertionError(message).args[0]; with no message, args is empty and the traceback shows a bare AssertionError
Syntax
assert is a statement: assert(x) works only because (x) is just x in parentheses; assert(x, msg) is the tuple trap

FAQ

assert (x > 0, "msg") asserts a two-item tuple, and non-empty tuples are always truthy. Write assert x > 0, "msg" — the comma belongs to the assert statement. CPython warns: "SyntaxWarning: assertion is always true, perhaps remove parentheses?".