# Stop ignoring asserts when running in optimized mode

**URL:** <https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132>\
**Category:** Ideas\
**Created:** [January 13, 2022, 8:04pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132 "2022-01-13T20:04:27Z")\
**Posts on this page:** 20\
**Page:** 3

<div class="post-metadata">

**Author:** ![PeterL](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/peterl/32/2076_2.png) [@PeterL](https://discuss.python.org/u/PeterL)\
**Post date:** [January 19, 2022, 5:11am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/41 "2022-01-19T05:11:37Z")

</div>

It may be useful to compare how C’s `assert` statement works.  
It is used in debug/testing phase for things that “should not happen”. (I like how @barry expressed it - “I never write `assert` s expecting them to _ever_ be triggered”). One asserts that something is true - if it’s not then something is severely broken _somewhere else_.  
Compiling in gcc or clang with -O_anything_ disables the asserts, so they have zero runtime effect in production.  
It makes sense to me for Python’s assert statement to have the same behaviour.

---

<div class="post-metadata">

**Author:** ![eryksun](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/eryksun/32/697_2.png) [@eryksun](https://discuss.python.org/u/eryksun)\
**Post date:** [January 19, 2022, 12:11pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/42 "2022-01-19T12:11:13Z")

</div>

> [@PeterL](#):
>
> Compiling in gcc or clang with -O _anything_ disables the asserts, so they have zero runtime effect in production.

The optimization level and debugger information level (e.g. symbols, source lines, etc) used by gcc and cl (MSVC) are unrelated to disabling debug assertions. I don’t know what clang does. For example, use gcc to compile a test program with `-O3` optimization that calls a function that contains the single statement `assert(0)`. Remember to `#include <assert.h>`. The program will still abort due to the assertion failure. Next compile it with `-DNDEBUG` to make the compiler ignore debug assertions. From the Linux man page for `assert`:

> If the macro `NDEBUG` is defined at the moment \<assert.h\> was last included, the macro assert() generates no code, and hence does nothing at all. It is not recommended to define `NDEBUG` if using `assert()` to detect error conditions since the software may behave non-deterministically.

From the POSIX specification of [`assert`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/assert.html):

> Forcing a definition of the name `NDEBUG`, either from the compiler command line or with the preprocessor control statement `#define NDEBUG` ahead of the `#include` statement, shall stop assertions from being compiled into the program.

Thus far, I’m in favor of keeping debug assertions enabled by default, as in C. I’m marginally in favor of making debug assertions independent of the optimization level, as in the C compilers that I’ve used, and adding a new `-X ndebug` option to disable ` __debug__ ` blocks and assertions. However, I don’t know enough about what has to change to realize this and what will be affected. I assume it requires updating py\_compile and compileall to support compiling in release mode and debug mode, which will in turn be reflected in the name of PYC files, in addition to the “opt-N” optimization level in the name.

---

<div class="post-metadata">

**Author:** ![PeterL](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/peterl/32/2076_2.png) [@PeterL](https://discuss.python.org/u/PeterL)\
**Post date:** [January 19, 2022, 10:30pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/43 "2022-01-19T22:30:27Z")

</div>

Ah yes; C’s assert is a macro, so is influenced by `NDEBUG`.  
Yes, me too, in favor of keeping debug assertions enabled by default, as in C.

---

<div class="post-metadata">

**Author:** ![notatallshaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/notatallshaw/32/10861_2.png) [@notatallshaw](https://discuss.python.org/u/notatallshaw)\
**Post date:** [January 20, 2022, 1:05am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/44 "2022-01-20T01:05:55Z")

</div>

> [@methane](#):
>
> The `-OO` option has same problem.  
> It can strip significant RAM usage when using heavily documented library (e.g. SQLAlchemy).

Also if you’re making an executable using something like pyinstaller this can usually save a minimum of 0.5 MB on the installer and sometimes a few MB of the produced binary.

---

<div class="post-metadata">

**Author:** ![apalala](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/apalala/32/683_2.png) [@apalala](https://discuss.python.org/u/apalala)\
**Post date:** [January 21, 2022, 8:19pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/45 "2022-01-21T20:19:16Z")

</div>

It seems that al wee need for now is the simplest option:

- A interpreter argument that makes assertions be retained independently of other options.

---

<div class="post-metadata">

**Author:** ![dimaqq](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dimaqq/32/372_2.png) [@dimaqq](https://discuss.python.org/u/dimaqq)\
**Post date:** [February 2, 2022, 12:00am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/46 "2022-02-02T00:00:48Z")

</div>

Featured as #1 Python Security Pitfall here:

> **[10 Unknown Security Pitfalls for Python](https://blog.sonarsource.com/10-unknown-security-pitfalls-for-python/)**
>
> In this blog post, we share 10 security pitfalls for Python developers that we encountered in real-world projects.

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [February 2, 2022, 12:40am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/47 "2022-02-02T00:40:06Z")

</div>

> [@dimaqq](#):
>
> Featured as #1 Python Security Pitfall here:

Just to note, the text doesn’t appear to imply that the order has any meaning, so it would be more accurate to say that it was one of the ten security pitfalls mentioned. Also, the article’s representation of “optimized mode” is rather vague and arguably misleading, implying that removing asserts is one side-effect rather than the documented purpose of the mode, and the example a spectacularly poor mis-use of `assert` as intended and documented, which the text does not really call out nearly strongly enough IMO.

---

<div class="post-metadata">

**Author:** ![markshannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/markshannon/32/72_2.png) [@markshannon](https://discuss.python.org/u/markshannon)\
**Post date:** [March 4, 2022, 3:51pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/48 "2022-03-04T15:51:38Z")

</div>

This might be a little off-topic, but would it make things better or worse if ` __debug__ ` could be changed at runtime?  
(Assuming no negative performance impact)

---

<div class="post-metadata">

**Author:** ![bwoodsend](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/bwoodsend/32/4027_2.png) [@bwoodsend](https://discuss.python.org/u/bwoodsend)\
**Post date:** [December 30, 2022, 4:11am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/49 "2022-12-30T04:11:42Z")

</div>

One thing that makes `assert` such a tempting shortcut for raising errors is that it so nicely explains itself in the stack-trace even without providing an error description. Short of displaying the given value of `port`, I don’t think that the following message could be any more helpful in telling you what you did wrong:

```python
Traceback (most recent call last):
  File "test.py", line 3, in <module>
    assert isinstance(port, numbers.Integral) and 0 <= port <= 65535
                                                  ^^^^^^^^^^^^^^^^^^
AssertionError

```

Compare to whenever you do an `if not condition: raise ProperExceptionType("...")`, the stack-trace will point to the `raise` line instead of the line containing the failing condition so the string you pass to the raised exception constructor must effectively paraphrase the condition in wordy fluffy English just to get something at least as informative as the assertion was. I agree that it’s still laziness - but it certainly had me dragging my feet for a long while after I discovered that `assert` isn’t for raising usage errors. (I also first had to make the startling _if you make your API more intuitive, you don’t need quite so many usage error checks_ discovery before turning 1 line assert statements into several line exceptions felt acceptable.)

One potential remedy that springs to mind (which I can’t decide if I like or not) is to add some kind of `.enforce()` method to the `bool()` class. i.e. You’d do:

```python
isinstance(port, numbers.Integral).enforce(TypeError, "optional message")
(0 <= port <= 65535).enforce(ValueError)

```

That way, at least the condition would be in the stack-trace so you could usually avoid having to add additional textual descriptions. Alternatively, I suppose we could just banish the idea that oneline `if` statements are a code-style blasphemy since that too leads to a stack-trace with the condition in it:

```python
if not condition: raise Error(
  "message here"
)

```

* * *

> So your proposal is for assertions to stop being assertions? Having assertions disappear when running under -O is fundamental to what makes them assertions.

This is only true if your interpretation of the word _assertion_ comes from being already familiar with C’s assert. To me, the word _assert_ means _check_ and not _check …oh, but skip over it if you’re in a hurry_. I don’t think that the current meaning of `assert` makes much sense outside the context of compiled languages. Python has no concept of compile time macros, constants or compiler directives (which is good!) so it seems odd that this single compile time setting exists. Whichever side of this fence you choose to sit on, having Python developers on both sides effectively makes both ideals unusable - you can’t use `-O` in case any one of your (indirect) dependencies uses asserts as error checks and you yourself can’t use `assert` for error checks because someone might run your code under `-O`. The only way to avoid trouble from both sides is to pretend that neither feature exists.

Does `-OO` ever make a significant difference to memory consumption? The most significant case I can find is Inada’s example of `import sqlalchemy` (even more so than NumPy which likes to write essay docstrings for even non-public and obvious oneliner functions) at about 9% savings in RSS (no I don’t know what the different memory stats mean) and negligible changes everywhere else. Bearing in mind that this is very much an upper bound - a realistic program does more than import libraries and the doing of stuff will eat more memory without loading more docstrings - is \<9% really worth having a separate mode for?

```python
> python memory-test.py sqlalchemy
rss 36.2 MB | vms 265.8 MB | shared 12.7 MB | text 4.1 kB | lib 0 Bytes | data 24.3 MB | dirty 0 Bytes
> python -OO memory-test.py sqlalchemy
rss 33.8 MB | vms 263.3 MB | shared 12.6 MB | text 4.1 kB | lib 0 Bytes | data 21.8 MB | dirty 0 Bytes

> python memory-test.py numpy
rss 38.4 MB | vms 601.1 MB | shared 18.0 MB | text 4.1 kB | lib 0 Bytes | data 316.2 MB | dirty 0 Bytes
> python -OO memory-test.py numpy
rss 35.6 MB | vms 597.9 MB | shared 18.0 MB | text 4.1 kB | lib 0 Bytes | data 312.9 MB | dirty 0 Bytes

```

> **Crude memory-test.py script**
>
> ```python
> import sys
> [__import__ (i) for i in sys.argv[1:]]
> import humanize
> import psutil
> print(*(f"{i} {humanize.naturalsize(j)}" for (i, j) in psutil.Process().memory_info()._asdict().items()), sep=" | ")
> 
> ```

Linux packages containing Python code (including Python itself) are installed in root owned locations meaning that the Python bytecode files need to be installed as part of the package. Needing 3 variants of pycache roughly doubles the installed footprint size. This rather stings on Alpine (where the whole distribution is designed to be runable in memory) - 18MB of the 48MB `python3` package is just _optimised_ bytecode that will likely never used. As a result, running Python in `-OO` mode in an in memory Alpine container actually consumes about 15MB more memory than if the optimised bytecode has been removed and Python is running in its normal mode. If `-OO` mode could instead read the unoptimised `.pyc` files but skip loading the docstrings then `-OO` would be an optimisation but currently, it’s an un-optimisation in container-land.

The _global_ nature of `-O` and `-OO` or _production_ and _non production_ is too coarse to be usable. To me, the _production environment_ for any package I write is anywhere that isn’t that package’s own test suite. I wouldn’t even want my assertions slowing down the test suites of dependent packages. Since that per-package mode doesn’t exist (and to be honest, I’m glad that it doesn’t), and I certainly don’t want to force people to use `-O` to get decent performance out of my code, make do without Python’s `assert` (usually I find that if I break my overly long functions up and move the checks into some low level unit tests, I’m happy without them).

The same is true for `-OO`. [pycparser](https://github.com/eliben/pycparser) which, due to it being a dependency of [cffi](https://cffi.readthedocs.io/en/latest/), is a classic [xkcd 2347](https://xkcd.com/2347/) project. It stores its parsing rules in docstrings which vanish under `-OO` thus making it and all its dependencies unusable under `-OO`. It even knocks out Damian’s point about running PyInstaller with `-OO` to reduce the application size. (Admittedly, here I think the correct solution is to change the underlying parser to not use docstrings. The poor maintainer is not convinced.)

Given all I say above (apologies for the length by the way), I’d personally be strongly in favour of removing both `-O` and `-OO` modes. I think they’re both micro optimisations on when the sun is shining and far too much trouble the rest of the time.

---

<div class="post-metadata">

**Author:** ![TeamSpen210](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/teamspen210/32/1004_2.png) [@TeamSpen210](https://discuss.python.org/u/TeamSpen210)\
**Post date:** [December 30, 2022, 8:23am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/50 "2022-12-30T08:23:20Z")

</div>

What if the optimisation modes were changed to become per-module, instead of global? The key problem seems to be that some packages may not be compatible with the optimisations, but application developers may want the slight improvements. I’m imagining some method of specifying a module/package name and one of the optimisation levels. That would then apply to it and all submodules (if any). When importing that is checked, and the module is compiled with ` __debug__ ` replaced with the appropriate `LOAD_CONST`. That should handle most usages of the constant, though dynamic access via `builtins` would be inconsistent.

This way libraries could advertise whether they do/do not support the optimisation levels, and then the author of an application could enable optimisations for all libraries that can handle it. It might be too much effort to be worth the small improvements the optimisation levels give though.

---

<div class="post-metadata">

**Author:** ![smontanaro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/smontanaro/32/1389_2.png) [@smontanaro](https://discuss.python.org/u/smontanaro)\
**Post date:** [December 30, 2022, 12:00pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/51 "2022-12-30T12:00:57Z")

</div>

> [@bwoodsend](#):
>
> One potential remedy that springs to mind (which I can’t decide if I like or not) is to add some kind of `.enforce()` method to the `bool()` class. i.e. You’d do:
> 
> ```auto
> isinstance(port, numbers.Integral).enforce(TypeError, "optional message")
> (0 <= port <= 65535).enforce(ValueError)
> 
> ```
> 
> That way, at least the condition would be in the stack-trace so you could usually avoid having to add additional textual descriptions.

You could define an enforce function which takes a boolean and an exception class. Mess around with that to see if you really like the idea.

---

<div class="post-metadata">

**Author:** ![bwoodsend](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/bwoodsend/32/4027_2.png) [@bwoodsend](https://discuss.python.org/u/bwoodsend)\
**Post date:** [December 30, 2022, 5:52pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/52 "2022-12-30T17:52:15Z")

</div>

Yeah, that’s true. Just fleshing it out, this

```python
import numbers

def enforce(ok, type, message=""):
    if not ok:
        raise type(message)

port = 12345432
enforce(isinstance(port, numbers.Integral), TypeError)
enforce(0 <= port <= 65535, ValueError, "Out of bounds port number")

```

Gives:

```python
Traceback (most recent call last):
  File "error.py", line 9, in <module>
    enforce(0 <= port <= 65535, ValueError, "Out of bounds port number")
  File "error.py", line 5, in enforce
    raise type(message)
ValueError: Out of bounds port number

```

The last frame in the stracktrace is distracting (there’s no way to address that short of writing `enforce()` in a C extension, right?) but the rest of the output is quite nice.

The one thing that really kills the idea for me though is that it’s dependent on formatting. If the `enforce()` call was written across multiple lines (which formatters like `black` will insist you do if the line gets too long) you get

```python
Traceback (most recent call last):
  File "error.py", line 9, in <module>
    enforce(
  File "error.py", line 5, in enforce
    raise type(message)
ValueError: Out of bounds port number

```

which is useless.

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [December 31, 2022, 2:01am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/53 "2022-12-31T02:01:26Z")

</div>

> [@bwoodsend](#):
>
> there’s no way to address that short of writing `enforce()` in a C extension, right?

You’re able to manipulate traceback stacks with the [`traceback`](https://docs.python.org/3/library/traceback.html) module.

At a higher level, however, I’m not sure you should care: I think either let downstream developers decide which frames to mentally ignore when reading the traceback, or don’t show tracebacks at all to end users (using `sys.tracebacklimit = 0`),

---

<div class="post-metadata">

**Author:** ![yonillasky](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/yonillasky/32/9804_2.png) [@yonillasky](https://discuss.python.org/u/yonillasky)\
**Post date:** [January 1, 2023, 9:47pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/54 "2023-01-01T21:47:38Z")

</div>

> You could define an enforce function which takes a boolean and an exception class. Mess around with that to see if you really like the idea.

The problem with the `enforce` function ideas, compared to `assert`, is that they require `message` to be evaluated up front. Asserts are supposed to “never happen” in normal use, and the message might be arbitrarily costly to produce.

This would be a non-issue if Python supported inlining. I don’t know if we can expect anything like that any time soon, though.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [January 1, 2023, 10:28pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/55 "2023-01-01T22:28:54Z")

</div>

> [@yonillasky](#):
>
> The problem with the `enforce` function ideas, compared to `assert`, is that they require `message` to be evaluated up front. Asserts are supposed to “never happen” in normal use, and the message might be arbitrarily costly to produce.

If it’s that costly to generate the message text, the classic `if ...: raise Ex(...)` syntax should be fine. Of course, if they really truoly “never happen”, then regular assertions are fine, since optimizing them out won’t be a problem.

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [January 2, 2023, 7:41am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/56 "2023-01-02T07:41:48Z")

</div>

If it’s that costly to generate the message text, and you hate the `if` statement, you can use the `or` operator:

```auto
def die(type, /, *args, **kwargs):
    raise type(*args, **kwargs)

port = 12345432
isinstance(port, numbers.Integral) or die(TypeError)
(0 <= port <= 65535) or die(ValueError, "Out of bounds port number")

```

---

<div class="post-metadata">

**Author:** ![vovavili](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vovavili/32/8848_2.png) [@vovavili](https://discuss.python.org/u/vovavili)\
**Post date:** [January 3, 2023, 1:17am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/57 "2023-01-03T01:17:11Z")

</div>

> [@steven.daprano](#):
>
> No, the caller of the code should (almost) never catch AssertionError

It’s useful when you want to trigger an alternative procedure on either an error or if some condition is not met. Here is a sample from one of my scripts, where I query cache either if it is out of date, if it does not exist, if it has been corrupted or if I force the update manually:

```auto
try:
    assert not force_update
    # Check whether the locally stored cache needs an update
    timestamp = Parser().load(timestamp_filename)
    # Only prune cache if new patch has been released
    if datetime.now() > datetime.fromisoformat(timestamp["timestamp"]):
        patch = get_latest_patch()
        assert patch == timestamp["patch"]
        update_timestamp(patch)
    # Load the locally stored cache, if it exists
    data = Parser().load(cache_filename)
except (FileNotFoundError, OSError, AssertionError, KeyError):
    ....

```

Technically, I could do the same with a tracking variable and `if...: raise ValueError` statement, but the way above does not offend my aesthetic taste in any way.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [January 3, 2023, 1:50am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/58 "2023-01-03T01:50:39Z")

</div>

> [@vovavili](#):
>
> Technically, I could do the same with a tracking variable and `if...: raise ValueError` statement, but the way above does not offend my aesthetic taste in any way.

Well, it’s broken code, so whether it offends your aesthetic taste or not, this code has to be assumed to be buggy. Even if a proposal like this goes through, it will be fragile code that will LOOK like it works on all versions of Python, but will be subtly broken on every version up to X.Y where the change happens.

TBH exception handling is a pretty poor way to handle `force_update` here anyway. Here’s how I would code that sort of logic:

```auto
def need_update():
    if force_update: return "forced"
    timestamp = Parser().load(timestamp_filename)
    if datetime.now() > datetime.fromisoformat(timestamp["timestamp"]):
        patch = get_latest_patch()
        if patch != timestamp["patch"]: return "patch"
        update_timestamp(patch)
    try:
        data = Parser().load(cache_filename)
    except (FileNotFoundError, OSError):
        return "not-found"
    return ""

if need_update():
    ...

```

(There’s now an encapsulation problem in that `data` is local to the function, but the precise solution to that depends on where you want to put the data. Alternatively, you could have the failure modes return None, as long as the data itself will never be None, and then check for that instead of looking for an empty cause-of-reload keyword. In any case, the logic is the same.)

There’s really no reason to use `assert` for this kind of check. Honestly, I don’t think that `raise ValueError` is right either, but it’s certainly less wrong than `assert`.

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [January 3, 2023, 5:25am UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/59 "2023-01-03T05:25:34Z")

</div>

At first glance, that looks like an abuse of AssertionError, and risky code (non-obvious bug).

I call it an abuse of AssertionError because assertions have a very clear set of well-established semantics. In the same way that we expect that SyntaxError should only be used for actual syntax errors, and ImportError should only be used for import errors, AssertionError should only be used for failed assertions and (maybe) failed tests (unit tests, regression tests, etc).

We would never use assertions in that way in Java, C/C++ or Eiffel, and we shouldn’t make a practice of it in Python either.

Whether your application runs with assertions switched on or not is not under your control as developer, it is under the control of the user running the application. If they are disabled, your application may break.

I think that perhaps the best way to think of `assert` is that it is a _checked comment_: they are comments to the reader that this statement is true, but the interpreter happens to (sometimes) actually check them at runtime too.

Checks which are expected to sometimes fail should not use `assert`, for the same reason you wouldn’t write a class that used the ` __eq__ ` special method to implement addition and the ` __add__ ` method to implement equality. Sure, it works, but it is bad code because it is misleading and surprising to the human reader.

Except that misusing operator overloading actually does work. Misusing `assert` risks breaking your code, if the end-user runs it with the `-O` commandline switch.

---

<div class="post-metadata">

**Author:** ![bwoodsend](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/bwoodsend/32/4027_2.png) [@bwoodsend](https://discuss.python.org/u/bwoodsend)\
**Post date:** [January 3, 2023, 1:42pm UTC](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132/60 "2023-01-03T13:42:09Z")

</div>

Even if `-O` was removed, I’d still consider that to be wrong. I prefer to handle the _error or false condition logic_ scenario with by using `return` to escape when the condition is true:

```python
try:
    if not force_update:
        upstream_timestamp = (whatever you need here)
        if cache.stat().st_mtime >= upstream_timestamp:
            # Everything up to date. Nothing to do
            return
except FileNotFoundError:
    pass
(refresh the cache here)

```

[Previous page](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132.md?page=2)

[Next page](https://discuss.python.org/t/stop-ignoring-asserts-when-running-in-optimized-mode/13132.md?page=4)
