You should NOT disable assertions. They catch unanticipated errors when attention is elsewhere. See Rule 5 in "The power of ten" (DOI, Wikipedia).
Write raise statements, instead of assert statements:
if x_is_broken():
raise RuntimeError('`x` is broken.')
The raise statements remain present, whatever the optimization options with which Python is run. Also, using raise statements enables specifying a type of exception different than AssertionError. This is very useful for users. Moreover, just writing a raise statement prompts to ask oneself whether AssertionError is the right choice there.
In addition, when writing a raise statement, we are lead to write an informative message, for example raise AssertionError('An error occurred with `x`.'). Writing an error message is possible in an assert statement (e.g., assert x, 'An error occurred with `x`.', and parentheses can be used for messages written over multiple lines), however, it can be forgotten. In contrast, raise AssertionError(....) requires that .... be filled (and the form raise AssertionError is unusual and not recommended).
When writing error messages, it is remarkable how many further coding errors will be revealed.
Sidenote: computationally expensive assertion checks can be run only when requested. One way is:
import logging
log = logging.getLogger(__name__)
if log.getEffectiveLevel() < logging.DEBUG:
if not check_expensive_property(x):
raise RuntimeError('`x` is broken.')
If this is set to a non-empty string it is equivalent
to specifying the -O option. If set to an integer, it is equivalent to
specifying -O multiple times.