Thank you
On Sat, Sep 25, 2021, at 18:53, Guido van Rossum wrote:
> First you need to fond a core dev to sponsor you (Steven D’A?). That person
> will guide you through the process.
>
> On Sat, Sep 25, 2021 at 08:30 Noam Tenne <n...@10ne.org> wrote:
>> __
>> So should I just scratch this and rewrite a PEP for an extensible assertion
>> mechanism?
>>
>> On Fri, Sep 24, 2021, at 14:04, Noam Tenne wrote:
>>>
>>> Hi All,
>>>
>>> Following the discussions on "Power Assertions: Is it PEP-able?", I've
>>> drafted this PEP.
>>> Your comments are most welcome.
>>>
>>> PEP: 9999
>>> Title: Power Assertion
>>> Author: Noam Tenne <n...@10ne.org>
>>> Status: Draft
>>> Type: Standards Track
>>> Content-Type: text/x-rst
>>> Created: 24-Sep-2021
>>>
>>>
>>> Abstract
>>> ========
>>>
>>> This PEP introduces a language enhancement named "Power Assertion".
>>>
>>> The Power Assertion is inspired by a similar feature found in the
>>> `Groovy language`_ and is an extension to the core lib's ``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.
>>>
>>> .. _Groovy language:
>>> http://docs.groovy-lang.org/next/html/documentation/core-testing-guide.html#_power_assertions
>>>
>>>
>>> Motivation
>>> =========
>>>
>>> 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.
>>>
>>> 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.
>>>
>>>
>>> The Community Wants This
>>> ------------------------
>>>
>>> As mentioned in the abstract, this feature was borrowed from Groovy.
>>> It is a very popular feature within the Groovy community, also used by
>>> projects such as `Spock`_.
>>>
>>> On top of that, it is very much needed in the Python community as well:
>>>
>>> * `Power Assertion was explicitly requested`_ as a feature in the
>>> `Nimoy`_ testing framework
>>> * There's a `similar feature in pytest`_
>>>
>>> And when `discussed in the python-ideas`_ mailing list, the responses
>>> were overall positive:
>>>
>>> * "This is cool." - Guido van Rossum
>>> * "I was actually thinking exactly the opposite: this would more
>>> useful in production than in testing."
>>> - 2QdxY4RzWzUUiLuE@potatochowder.com
>>> * "What pytest does is awesome. I though about implementing it in the
>>> standard compiler since seen it the first time." - Serhiy Storchaka
>>>
>>> .. _Spock: https://spockframework.org/
>>> .. _Power Assertion was explicitly requested:
>>> https://stackoverflow.com/a/58536986/198825
>>> .. _similar feature in pytest:
>>> https://docs.pytest.org/en/latest/how-to/assert.html
>>> .. _discussed in the python-ideas:
>>> https://mail.python.org/archives/list/python-ideas@python.org/thread/T26DR4BMPG5EOB3A2ELVEWQPYRENRXHM/
>>>
>>>
>>> Rational
>>> ========
>>>
>>> Code Example
>>> ------------
>>>
>>> ::
>>>
>>> class SomeClass:
>>> def __init__(self):
>>> self.val = {'d': 'e'}
>>>
>>> def __str__(self):
>>> return str(self.val)
>>>
>>> sc = SomeClass()
>>>
>>> assert sc.val['d'] == 'f'
>>>
>>> This routine will result in the output:
>>>
>>> ::
>>>
>>> Assertion failed:
>>>
>>> sc.val['d'] == f
>>> | | |
>>> | e False
>>> |
>>> {'d': 'e'}
>>>
>>>
>>> Display
>>> -------
>>>
>>> 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.
>>>
>>> This way of presentation is clearer and more human friendly than the
>>> output offered by pytest's solution.
>>>
>>> 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.
>>>
>>>
>>> Mechanics
>>> ---------
>>>
>>> Reference Implementation
>>> ''''''''''''''''''''''''
>>>
>>> The reference implementation uses AST manipulation because this is
>>> the only way that this level of involvement can be achieved by a
>>> third party library.
>>>
>>> It iterates over every subexpression in the assert statement.
>>> Subexpressions are parts of the expression separated by a lookup
>>> (``map[key]``), an attribute reference (``key.value``) or a binary
>>> comparison operator (``==``).
>>>
>>> It then builds an AST in the structure of a tree to maintain
>>> the order of the operations in the original code, and tracks the
>>> original code of the subexpression together with the AST code of the
>>> subexpression and the original indices.
>>>
>>> It then rewrites the AST of the original expression to call a
>>> specialised assertion function, which accepts the tree as a parameter.
>>>
>>> At runtime the expression is executed. If it fails, a rendering
>>> function is called to render the assertion message as per the example
>>> above.
>>>
>>>
>>> Actual Implementation
>>> '''''''''''''''''''''
>>>
>>> To be discussed.
>>>
>>> In the python-ideas mailing list, Serhiy Storchaka suggests:
>>>
>>> It needs a support in the compiler. The condition expression should be
>>> compiled to keep all immediate results of subexpressions on the stack.
>>> If the final result is true, immediate results are dropped. If it is
>>> false, the second argument of assert is evaluated and its value together
>>> with all immediate results of the first expression, together with
>>> references to corresponding subexpressions (as strings, ranges or AST
>>> nodes) are passed to the special handler. That handler can be
>>> implemented in a third-party library, because formatting and outputting
>>> a report is a complex task. The default handler can just raise an
>>> AttributeError.
>>>
>>>
>>> Caveats
>>> -------
>>>
>>> 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.
>>>
>>>
>>> Reference Implementation
>>> ========================
>>>
>>> There's a `complete implementation`_ of this enhancement in the
>>> `Nimoy`_ testing framework.
>>>
>>> It uses AST manipulation to remap the expression to a `data structure`_
>>> at compile time, so that it can then be `evaluated and printed`_ at runtime.
>>>
>>> .. _Nimoy: https://browncoat-ninjas.github.io/nimoy/
>>> .. _complete implementation:
>>> https://browncoat-ninjas.github.io/nimoy/examples/#power-assertions-beta
>>> .. _data structure:
>>> https://github.com/browncoat-ninjas/nimoy/blob/develop/nimoy/ast_tools/expression_transformer.py#L77
>>> .. _evaluated and printed:
>>> https://github.com/browncoat-ninjas/nimoy/blob/develop/nimoy/assertions/power.py
>>>
>>>
>>> Copyright
>>> =========
>>>
>>> This document is placed in the public domain or under the
>>> CC0-1.0-Universal license, whichever is more permissive.
>>>
>>>
>>>
>>> ..
>>> Local Variables:
>>> mode: indented-text
>>> indent-tabs-mode: nil
>>> sentence-end-double-space: t
>>> fill-column: 70
>>> coding: utf-8
>>> End:
>>>
>>> *Attachments:*
>>> * pep-9999.rst
>> _______________________________________________
>> 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/DO3ETJ4COXAFJKNYZP3IAGFX5N64CILD/
>> 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/7C6BWQN6LTPKJJZGEFMPT3TBFHIH3MHW/
Code of Conduct: http://python.org/psf/codeofconduct/