>
> 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 <gu...@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 <n...@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-le...@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-le...@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*
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/RHXMGHUXNSHJTDBNAI3ZQGWZXN2TDFLN/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to