Stop ignoring asserts when running in optimized mode

The problem is that I want to re-use patch variable a couple of lines of code below in the except clause (if I have not received an error by the time it has been populated), so returning early leads to some complexity if I use encapsulation.

I fully agree with what Steven D’Aprano is saying in here. He’s hit the nail on the head.

If you use assertions as intended, there should be no concern about the optimizer removing them.

If I start to put write code inside my docstrings and write a parse and executer for it. Should I complain about the -OO option removing docstrings? (no, someone should set me straight)

This thread feels focused on the wrong problem. The issue isn’t the optimizer, the issue is misuse of assertions.

If you use assertions as intended, there should be no concern about the optimizer removing them.

It’s not just you though - it’s every single developer for each dependency in you dependency tree that you need to convince that this ideology is worth adhering to.

I’m very confused - what ideology? The “ideology” that you write code that doesn’t assume language constructs work differently than they are documented as doing? It’s not an ideology to read the language spec and follow it…

5 Likes

The ideology here is: Bugs need to be found and fixed. It doesn’t make a difference where the bug is, we need to fix it. (Ned Batchelder, in his PyCon keynote, pointed out that this is true even of things that aren’t software; notably, our interactions with other people are every bit as important as API calls between programs, and if we mess them up, that’s a bug to be fixed.) When there’s a very common problem across many programmers, it sometimes helps for the language to give hints (see, for example, the SyntaxWarning when you type if txt is "word": in recent Pythons), but it’s very very rare for the language to be changed in response to buggy usage.

The job of assertions is to be checkable declarations that, conceptually, can’t ever fail. It’s safe to skip them in production because they’ll never fail anyway. Now, we’re flawed programmers and we write flawed code, so sometimes they WILL fail in prod; but at very least, our intent should be that they don’t fail.

So what do you do when a programmer’s intent doesn’t line up with the code that’s been written? You change the code - or file a bug report, if it’s not your code - to improve the code. Assertions can be straight-forwardly replaced with conditional exception raising, thus making the code better.

The ideology we’re following is: The world is a really really messy place, and we’re a team of underpaid janitors trying our best to clean things up. Let’s work together to make the world just a tiny bit tidier.

2 Likes

Rust makes the difference explicit with assert! and debug_assert! macros. assert! always runs, debug_assert! only runs in non-optimised builds. If we’re uncomfortable just deprecating -O, something similar could be a fairly viable path forward for Python:

  • assert condition will always raise AssertionError if condition does not hold
  • debug_assert condition by default is a no-op, but functions the same as assert when run under -X dev

I agree that there’s a nice alternate universe where AssertionError is a subclass of BaseException, to discourage use in control flow. Too bad.

Overall, I’d be happy to do nothing here, but it is a shame that I’ve seen a lot of people seed fear into beginners’ minds about assert because of a flag that maybe <0.01% of Python programmers use. assert is great. It’s self documenting and ergonomic and helps me make sure my code keeps the invariants I intend it to keep.

5 Likes

I think this is mostly about educating people about when to use assert and why its usually better to use regular if-statements for checks which are not assertions (ie. not checking invariants).

IMO, the topic’s suggestion is going in a wrong direction: It tries to avoid educating people by making assert behave just like a regular if-statement.

5 Likes

If done, it would be better to make this non-breaking by keeping assert as it is (i.e. equivalent to Rust’s debug_assert!) and introduce a new always-firing assert. (For example strict_assert, or maybe something like assert! or +assert to not introduce a new keyword?)

I’d be inclined to use a completely different word for it, to avoid confusion like “asserts might not run, but there’s these special kinds of asserts that will”. Instead, it would be “assertions are things that you can safely skip, and here’s a way to establish contracts that will always be run”.

But if you want that, we already have a way to write it. It’s an if statement and a raise. So you’d have to show that it’s better to abbreviate this in some way.

1 Like

There’s too much bike-shedding here.

Either stop ignoring asserts or continue status quo.

As mostly an outside observer of this thread, I don’t see the discussion as bikeshedding, but rather exploring the problem space and discussing mechanisms to address the problems presented in the OP and followup discussion. I’m not sure it is fair to simply dismiss at all that on the grounds of “bikeshedding”.

Debating, e.g., the spelling of assert, -OO or a hypothetical debug_assert or strict_assert proposed by some would be bikeshedding, if not directly relevant to solving the problem but the discussion has focused on the intersection of language behavior, user behavior and how to synchronize the two (which can involve some naming discussion, where relevant to the user assumptions at play here).

If one is aware of the -OO problem, it’s easy enough to assign to a variable, which never gets stripped, and set __doc__ at run time. Just sent README.rst: recommend python -OO compatible usage by cben · Pull Request #515 · docopt/docopt · GitHub to document a safe recipe there.

docopt doesn’t actually care what’s the script’s docstring, it takes a string as parameter and it’s merely convention to reuse the docstring for both purposes.

There are libs however, like doctest, that really inspect modules/classes/functions for their __doc__. (Again, assigning func.__doc__ at run-time is possible, but becomes awkward if you do it a lot.)

@ewjoachim

Displaying debug information in a production environment presents security risks. Debug information frequently includes sensitive details about the application’s internal processes, the system, or the infrastructure. This information could be exploited by malicious individuals to gain unauthorized access or carry out other harmful activities.

If you think that using AssertionError will make it more secure, just refrain from doing so.

I use asserts to tell my type-checker that my type is something when it cant tell that itself (assert isinstance(x, Foo) all over my code.

if this was changed then this would add extra cost to my code and sometimes break my code as well.

i do not think asserts should be ran when in optimized mode.

1 Like

I wouldn’t worry about it, this discussion is a year and a half old but is being resurrected occasionally for some reason. Probably should be locked.

1 Like