What about asserts that are not used for testing, but as classic “unless there’s a bug, this should hold”?

To me this relates to the thread about having a structured assert that doesn't go away with -O.

My purpose when addressing assert was precisely the “unless there’s a bug, this should hold” kind of assertions.

In that context, introducing additional yet known costs (as in this "power" idea), providing for different exception types (maybe all descending from AssertError?), and allowing for a block to prepare the exception, are all worth it.

Introducing the new syntax for assert would imply zero cost for existing assertions.

On Sun, Sep 12, 2021 at 10:28 AM Guido van Rossum <guido@python.org> wrote:
This is cool.

AFAIK pytest does something like this. How does your implementation differ?

What is your argument for making this part of the language? Why not a 3rd party library?

What about asserts that are not used for testing, but as classic “unless there’s a bug, this should hold”? Those may not want to incur the extra cost. 

—Guido

On Sun, Sep 12, 2021 at 07:09 <noam@10ne.org> wrote:
Hi all,

I’d like your comments and feedback on an enhancement that introduces power assertions to the Python language.

Proposal
--------
This feature is inspired by a similar feature of the Groovy language[1], and is effectively a variant of the `assert` keyword.
When an assertion expression evaluates to `False`, the output shows not only the failure, but also a breakdown of the evaluated expression from the inner part to the outer part.

For example, a procedure like:
```python
class SomeClass:
    def __init__(self):
        self.val = {'d': 'e'}

    def __str__(self):
        return str(self.val)

sc = SomeClass()

assert sc.val['d'] == 'f'
```

Will result in the output:

```python
Assertion failed:

sc.val['d'] == f
|  |        |
|  e        False
|
{'d': 'e'}
```
See link [2] if the formatting above is screwed up.

In the output above we can see the value of every part of the expression from left to right, mapped to their expression fragment with the pipe (`|`).
The number of rows that are printed depend on the value of each fragment of the expression.
If the value of a fragment is longer than the actual fragment (`{'d': 'e'}` is longer than `sc`), then the next value (`e`) will be printed on a new line which will appear above.
Values are appended to the same line until it overflows in length to horizontal position of the next fragment.

The information that’s displayed is dictated by the type.
If the type is a constant value, it will be displayed as is.
If the type implements `__str__`, then the return value of that will be displayed.

It is important to note that expressions with side effects are affected by this feature. This is because in order to display this information, we must store references to the instances and not just the values.

Rational
--------
Every test boils down to the binary statement "Is this true or false?", whether you use the built-in assert keyword or a more advanced assertion method provided by a testing framework.
When an assertion fails, the output is binary too — "Expected x, but got y".

There are helpful libraries like Hamcrest which give you a more verbose breakdown of the difference and answer the question "What exactly is the difference between x and y?".
This is extremely helpful, but it still focuses on the difference between the values.

We need to keep in mind that a given state is normally an outcome of a series of states, that is, one outcome is a result of multiple conditions and causes.
This is where power assertion comes in. It allows us to better understand what led to the failure.

Implementation
--------
I’ve already built a fully functional implementation[2] of this feature as part of my Python testing framework - Nimoy[3].
The current implementation uses AST manipulation to remap the expression to a data structure[4] at compile time, so that it can then be evaluated and printed[5] at runtime.


[1] http://docs.groovy-lang.org/next/html/documentation/core-testing-guide.html#_power_assertions
[2] https://browncoat-ninjas.github.io/nimoy/examples/#power-assertions-beta
[3] https://github.com/browncoat-ninjas/nimoy/
[4] https://github.com/browncoat-ninjas/nimoy/blob/develop/nimoy/ast_tools/expression_transformer.py#L77
[5] https://github.com/browncoat-ninjas/nimoy/blob/develop/nimoy/assertions/power.py
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/T26DR4BMPG5EOB3A2ELVEWQPYRENRXHM/
Code of Conduct: http://python.org/psf/codeofconduct/
--
--Guido (mobile)
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EEVHXKWUMVSEPAR73WOBQM3BO7NESPBZ/
Code of Conduct: http://python.org/psf/codeofconduct/


--
Juancarlo Añez