[Python-ideas] Repurpose `assert' into a general-purpose check

Steven D'Aprano steve at pearwood.info
Tue Jan 16 05:23:27 EST 2018

On Tue, Jan 16, 2018 at 01:38:25AM -0800, smarie wrote:

> Typically ‘assert isfinite(x)‘ is today a good example of what is wrong: 
>    1. it can be disabled globally by end-users even if the lib developer 
>    does not want it, 

That's not a bug, or even a problem ("wrong"), it is the very purpose of 
assert. Assertions are intended to allow the end user to disable the checks.

If you, the developer, don't want a check to be disabled, then you 
shouldn't call it an assertion and use assert.

I sometimes wish that Python included a richer set of assertions rather 
than just a single `assert` keyword. Something like Eiffel's concept of 
pre-conditions, post-conditions and invariants, where each can be 
enabled or disabled independently. But even with Python's sparse 
assertion API, the reason for assert is for debugging checks which can 
optionally be disabled.

Checks which should never be disabled don't belong as assertions.

>    2. if x is None the exception is different from the exception you get if 
>    x is not finite

This is a little untidy, but I don't see why this should be considered 
"wrong". That's just the way Python operates: however you write a check, 
it is going to rely on the check succeeding, and if it raises an 
exception, you will see that exception.

That's a good thing, not "wrong" -- if my assertion isfinite(x) fails 
because x is None, I'd much rather see a TypeError than

   AssertionError: x is not finite

>    3. you can not customize the exception type for easy error codes 
>    internationalization, you can only customize the message.

assert is not intended to be used to present user-friendly error 
messages to the end-user. The end-user should never see an 
AssertionError, and if they do, that's a bug, not a recoverable error or 
an avoidable environmental/input error.

I have come to the conclusion that a good way to think about assertions 
is that they are often "checked comments". Comments like:

    # when we get here, x is a finite number

quickly get out of sync with code. To quote Michael Foord:

    "At Resolver we've found it useful to short-circuit any doubt
    and just refer to comments in code as 'lies'. "

    -- Michael Foord paraphrases Christian Muirhead on python-dev, 

Instead, turn it into an assertion, if you can:

    assert isfinite(x)

which is both inline developer documentation ("x is a finite number") 
and a debugging aid (it will fail during development is x is not a 
finite number). And, if the cost of those assertions becomes excessive, 
the end-user can disable them.

> My proposal would be to rather define another statement for example 
>       'validate <expression> <exception, exception_type or 
> exception_message>'

The problem with a statement called "validate" is that it will break a 
huge number of programs that already include functions and methods using 
that name.

But apart from the use of a keyword, we already have a way to do almost 
exactly what you want:

    if not expression: raise ValidationError(message)

after defining some appropriate ValidationError class. And it is only a 
few key presses longer than the proposed:

    validate expression, ValidationError, message


More information about the Python-ideas mailing list