Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] assert: keep "where … and …" output in verbose mode #5933

Closed
wants to merge 4 commits into from

Conversation

blueyed
Copy link
Contributor

@blueyed blueyed commented Oct 9, 2019

Ref: #5932
Fixes: #5192

@blueyed
Copy link
Contributor Author

blueyed commented Oct 9, 2019

This is quite hacky/involved.
I think it might be better to replace something like "~%(default)" instead, which would avoid having to pass through the original repl.
OTOH it might be useful in custom hooks.

@blueyed
Copy link
Contributor Author

blueyed commented Oct 9, 2019

/cc @darrenburns via https://github.com/darrenburns/pytest-clarity (#5931)
I guess your feedback would be helpful here.. :)

@JBKahn
Copy link

JBKahn commented Oct 9, 2019

Changing pytest-clarity to use return expl + "\n~" + "\n~".join([utf8_replace(line) for line in output]) like in your example is a huge improvement.

======================================================================= FAILURES =======================================================================
________________________________________________________________________ test_a ________________________________________________________________________

    def test_a():
>       assert len(a) == len(b)
E       assert 3 == 2
E        +  where 3 = len([1, 2, 3])
E        +  and   2 = len([1, 2])
E         left == right failed.
E         Showing split diff:
E
E         left:  3
E         right: 2

test_file.py:5: AssertionError
________________________________________________________________ test_bigger_assertion _________________________________________________________________

    def test_bigger_assertion():
        first_dict = {a: a for a in range(100)}
        second_dict = {b: b for b in range(1, 101)}
>       assert first_dict == second_dict
E       assert {0: 0, 1: 1, 2: 2, 3: 3, ...} == {1: 1, 2: 2, 3: 3, 4: 4, ...}
E         left == right failed.
E         Showing unified diff (L=left, R=right):
E
E          L {0: 0,
E          L  1: 1,
E          R {1: 1,
E            2: 2,
…
E            97: 97,
E            98: 98,
E          L  99: 99}
E          R  99: 99,
E          R  100: 100}
E
E         1 items in left, but not right:
E         + (0, 0)
E         1 items in right, but not left:
E         - (100, 100)

test_file.py:10: AssertionError
================================================================== 2 failed in 0.05s ===================================================================

@darrenburns
Copy link
Contributor

This would be really cool! I personally prefer this approach over the templating suggestion, but I guess it may break backwards compatibility since the hook interface will change?

Copy link
Member

@nicoddemus nicoddemus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I much prefer this approach over templating... hooks are kept backward compatible if we introduce new parameters, so let's go for it. 👍

@@ -472,7 +472,7 @@ def pytest_unconfigure(config):
# -------------------------------------------------------------------------


def pytest_assertrepr_compare(config, op, left, right):
def pytest_assertrepr_compare(config, op, left, right, expl):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the docstring with expl as well. 😁

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would need more updating in general then, especially for when we go with the return-string-to-skip special postprocessing approach.

@@ -146,7 +148,7 @@ def assertrepr_compare(config, op, left, right):
type_fn = (isdatacls, isattrs)
explanation = _compare_eq_cls(left, right, verbose, type_fn)
elif verbose > 0:
explanation = _compare_eq_verbose(left, right)
return expl + "\n~" + "\n~".join(_compare_eq_verbose(left, right))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't get the "\n~" part...? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicoddemus
Copy link
Member

but I guess it may break backwards compatibility since the hook interface will change?

No, pluggy is smart and discards parameters that an implementation doesn't declare. 😉

@blueyed
Copy link
Contributor Author

blueyed commented Oct 9, 2019

Why do you both prefer this over a special template var (keep in mind that you have to use the "mini template language" here already).
It requires you to be aware of internals more, including that you have to prefix things with "~"/">".
It also clearly is a hack to handle strings in a special way (to skip post-processing).

@nicoddemus
Copy link
Member

Oh I didn't realize we had a template language in place, sorry for having given my opinion so soon. I will have to take a deeper look later then.

@blueyed blueyed changed the title [WIP/RFC] assert: keep "where … and …" output in verbose mode [RFC] assert: keep "where … and …" output in verbose mode Oct 16, 2019
@blueyed blueyed added topic: rewrite related to the assertion rewrite mechanism type: bug problem that needs to be addressed labels Oct 16, 2019
blueyed added a commit to blueyed/pytest that referenced this pull request Oct 21, 2019
TODO:

- [ ] real plugin hook?
- [ ] should handle other places where safeformat is used also, via
      args/options to the hook then likely.

Ref: pytest-dev#3962
Ref: pytest-dev#5933
@blueyed
Copy link
Contributor Author

blueyed commented Oct 21, 2019

@nicoddemus
Could you make up your mind?

(it is a long-standing regression, so it would be great to make some progress here)

@blueyed
Copy link
Contributor Author

blueyed commented Oct 21, 2019

Who can we ping about this - being more into the assertion rewriting part of pytest?

@nicoddemus
Copy link
Member

nicoddemus commented Oct 23, 2019

Could you make up your mind?

Sorry about the delay, I've been busy with other things.

The problem that I have with the expl parameter is that the user can't really do anything with it in practice:

def y():
    return [0, 1]

def test():
    assert y() == [1, 2, 3, 4]

For this test failure, the expl parameter is:

"%(py2)s\n{%(py2)s = %(py0)s()\n} == %(py5)s"

So the expl parameter of pytest_assertrepr_compare is only really useful for pytest's own internal implementation, assertrepr_compare.

I see two options:

  1. We have to rethink how pytest_assertrepr_compare actually works so users can reuse the variables saved by pytest's rewriting mechanism.

  2. We need to somehow pass expl from the rewriting mechanism directly to assertrepr_compare somehow.

I wouldn't even know where to start with 1), and 2) would require some hacking, like setting expl to a global.

At first look 2) is ugly, but we might evaluate it under the light that we already are doing something similar by installing util._reprcompare during pytest_runtest_setup.

What if we set util._reprcompare_expls during _call_reprcompare?

    if util._reprcompare is not None:
        util._reprcompare_expl = expl
        try:
            custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1])
        finally:
            util._reprcompare_expl = None
        if custom is not None:
            return custom

Again, while this is ugly, at least it is kept internal and won't introduce a parameter which we might regret later.

What do you think?

@blueyed
Copy link
Contributor Author

blueyed commented Oct 31, 2019

The problem that I have with the expl parameter is that the user can't really do anything with it in practice:

Yes.

What if we set util._reprcompare_expls during _call_reprcompare?

How does that help plugins with using the default?

@nicoddemus
Copy link
Member

nicoddemus commented Nov 6, 2019

How does that help plugins with using the default?

Unless I'm misunderstanding, I thought the patch was about to fix "keep "where … and …" output in verbose mode" in the pytest core, not necessarily expose to plugins the new "expls" parameter.

In my previous post, assertrepr_compare would access the global util._reprcompare_expl instead of receiving it by argument... again a hack, but we can avoid exposing a expl parameter to users, which would require us to carry this over many releases if we ever find a better way to represent the explanation obtained by the rewriter.

(Btw sorry for the delay, I am on vacation and now I'm trying to catch up with everything).

@blueyed
Copy link
Contributor Author

blueyed commented Nov 9, 2019

@nicoddemus
The idea here is to also make this available for plugins / do it "properly". See previous comment: #6104

@nicoddemus
Copy link
Member

The idea here is to also make this available for plugins / do it "properly". See previous comment: #6104

Is that the right issue number? 🤔

@blueyed
Copy link
Contributor Author

blueyed commented Nov 22, 2019

The idea here is to also make this available for plugins / do it "properly". See previous comment: #6104

Is that the right issue number?

No, sorry. More likely #5932.

@blueyed blueyed closed this Nov 27, 2019
@JBKahn
Copy link

JBKahn commented Nov 27, 2019

@blueyed does closing this mean no charge coming?

@blueyed
Copy link
Contributor Author

blueyed commented Nov 28, 2019

@JBKahn I've closed all my open PRs for now. Feel free to pick it up, I might come back to it later myself, but just had to clean up things for myself.

@JBKahn
Copy link

JBKahn commented Nov 28, 2019

@nicoddemus any more thoughts on this one? We'd really like to be able to use the explanation in the rewrite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: rewrite related to the assertion rewrite mechanism type: bug problem that needs to be addressed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Failed test output is less verbose with -v than not using -v
4 participants