question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Improve recursive diff output for comparison asserts on dataclasses/attrs

See original GitHub issue

pytest 6 will have a new feature (PR #6835) for displaying nicer diff output on large dataclasses. Often dataclasses have other dataclasses or other types nested inside them, and the assertion fails because one or more deeply nested fields differ. Before the diff was just on the top-level repr of the objects, which was very hard to decipher for larger dataclasses. After the PR, the output looks like this (see https://github.com/pytest-dev/pytest/pull/6835#issuecomment-637545552):

    def test_cmp():
>       assert obj == obj.replace(assentior=obj.assentior.replace(culpa=7, aliquip=Aliquip.Aliquip3))
E       AssertionError: assert Dignissim(assentior=Assentior(aliquip=<Aliquip.Aliquip1: 1>, culpa=6, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None), new_cupidatat=13, laoreet=1, rationibus=False) == Dignissim(assentior=Assentior(aliquip=<Aliquip.Aliquip3: 3>, culpa=7, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None), new_cupidatat=13, laoreet=1, rationibus=False)
E         Matching attributes:
E         ['new_cupidatat', 'laoreet', 'rationibus']
E         Differing attributes:
E         assentior: Assentior(aliquip=<Aliquip.Aliquip1: 1>, culpa=6, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None) != Assentior(aliquip=<Aliquip.Aliquip3: 3>, culpa=7, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None)
E         
E         Drill down into differing attribute assentior
E         Matching attributes:
E         ['fugiat',
E          'invenire',
E          'deserunt',
E          'lorem',
E          'mollit',
E          'laborums',
E          'tantas',
E          'nominati',
E          'fabulas',
E          'tritani']
E         Differing attributes:
E         aliquip: <Aliquip.Aliquip1: 1> != <Aliquip.Aliquip3: 3>
E         
E         Drill down into differing attribute aliquip
E         +<Aliquip.Aliquip1: 1>
E         -<Aliquip.Aliquip3: 3>
E         culpa: 6 != 7
E         
E         Drill down into differing attribute culpa
E         +6
E         -7

x.py:169: AssertionError

This is a great improvement, but the drill down structure is a bit hard to see. In this case particularly, there are two differing nested attributes, aliquip and culpa, but the culpa one starts just after the output of the aliquip without any spacing, which is confusing. Also, it is not visible at which nesting level the differing attribute is.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
bluetechcommented, Jun 24, 2020

This would be an improvement. I noticed the CONTRIBUTING.rst document doesn’t mention improvement, let me fix that 😃

1reaction
bluetechcommented, Jun 10, 2020

One idea is to use indentation for indicating the nesting level, like this:

    def test_cmp():
>       assert obj == obj.replace(assentior=obj.assentior.replace(culpa=7, aliquip=Aliquip.Aliquip3))
E       AssertionError: assert Dignissim(assentior=Assentior(aliquip=<Aliquip.Aliquip1: 1>, culpa=6, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None), new_cupidatat=13, laoreet=1, rationibus=False) == Dignissim(assentior=Assentior(aliquip=<Aliquip.Aliquip3: 3>, culpa=7, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None), new_cupidatat=13, laoreet=1, rationibus=False)
E         Matching attributes:
E         ['new_cupidatat', 'laoreet', 'rationibus']
E         Differing attributes:
E         assentior: Assentior(aliquip=<Aliquip.Aliquip1: 1>, culpa=6, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None) != Assentior(aliquip=<Aliquip.Aliquip3: 3>, culpa=7, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None)
E         
E             Drill down into differing attribute assentior
E             Matching attributes:
E             ['fugiat',
E              'invenire',
E              'deserunt',
E              'lorem',
E              'mollit',
E              'laborums',
E              'tantas',
E              'nominati',
E              'fabulas',
E              'tritani']
E             Differing attributes:
E             aliquip: <Aliquip.Aliquip1: 1> != <Aliquip.Aliquip3: 3>
E         
E                 Drill down into differing attribute aliquip
E                 +<Aliquip.Aliquip1: 1>
E                 -<Aliquip.Aliquip3: 3>
E
E             culpa: 6 != 7
E         
E                 Drill down into differing attribute culpa
E                 +6
E                 -7

x.py:169: AssertionError

That’s just one possible idea.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Recursively compare two directories with diff -r without output ...
Currently, diff treats symbolic links like regular files; it treats other special files like regular files if they are specified at the top ......
Read more >
Recursive diff on directories says files differ, but no output ...
1 Answer 1 ... I think your --brief is the problem ... without this option also it will only compare files with same...
Read more >
diff Options (Comparing and Merging Files) - GNU.org
Below is a summary of all of the options that GNU diff accepts. ... For example, recursive comparison of d to e might...
Read more >
Diff files present in two different directories - Stack Overflow
You can use the diff command for that: diff -bur folder1/ folder2/. This will output a recursive diff that ignore spaces, with a...
Read more >
diff - Compare two text files and show the differences - IBM
When comparing two directories, diff does not compare character special files, or FIFO special files with any other files. By default, output consists...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found