If assertions have an associated block, then `pdb` can be invoked within. I almost never use debuggers, so I don't remember, but I think a recent Python version introduced the likes of `debug()` to step into the pre-configured debugger. About the "power assertions" proposal in this thread, once everything is in place to produce the output as proposed, then libraries may come in to produce different output formats. My personal preference is for a form of assertions that don't go away, ever. Regarding the syntax, I think that should be the last part of the design. If you think about it, a block associated with the assert should execute when the assertion fails, so maybe it should be something like: *assert* *cond* *else:* *block* with an AssertionError raised if *"block"* does not raise. Another proposal out there that has the same semantics is: *unless* *cond*: *block* Yet I find that using a negative keyword makes things ugly. I'd rather stick to "*assert*", or maybe go for "*invariant*" statement, to be rid of the legacy of the meaning of *"assert"* in Python's history. *invariant* *cond*: *block* After all, I think it's invariants what I've been after in my recent posts. It's easy to document that the *"cond"* must be cheap because it will *_always_* be executed, and that the *"block"* can be as complex as required by the severity of the failure. I still think that there's different levels of criticality to failed assertions, and that an exception hierarchy that has levels can help build more reliable systems. On Tue, Sep 14, 2021 at 5:52 PM Finn Mason <finnjavier08@gmail.com> wrote:
I think that this is a great idea. However, pipes only point to one character, which can be confusing. (Citation: many tracebacks before 3.10.)
I'm wondering: Could failed assertions step you into `pdb`, if they are used for testing purposes? Could there be a way to specify different levels of assertions? For example, maybe certain assertions are turned off with -O or -OO, others turned off only with -O, and some that never are? There are lots of ways `assert` could be improved, and the question is how? What is/are the best way(s)?
-- Finn Mason
On Mon, Sep 13, 2021, 5:36 AM Juancarlo Añez <apalala@gmail.com> wrote:
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#... [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/expre... [5] https://github.com/browncoat-ninjas/nimoy/blob/develop/nimoy/assertions/powe... _______________________________________________ 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/T26DR4... 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/EEVHXK... 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-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/RHXMGH... Code of Conduct: http://python.org/psf/codeofconduct/
-- Juancarlo *Añez*