simplistix/testfixtures

Missing None Attributes Guard in testfixtures.comparison.compare_simple

Closed this issue · 2 comments

With testfixtures 6.18.2 installed, the following code raises an AttributeError on CPython 3.9.5:

import re
import testfixtures

shared_prefix = "a" * 199
x = re.compile(shared_prefix + "x")
y = re.compile(shared_prefix + "y")

try:
    testfixtures.compare(x, y)
except AssertionError:
    print("caught expected assertion error")

Output:

Traceback (most recent call last):
  File "/Users/ludwigc/Library/Application Support/Jetbrains/PyCharm2021.3/scratches/scratch.py", line 9, in <module>
    testfixtures.compare(x, y)
  File "/Users/ludwigc/.pyenv/versions/search-middleware/lib/python3.9/site-packages/testfixtures/comparison.py", line 697, in compare
    if not context.different(x, y, not_there):
  File "/Users/ludwigc/.pyenv/versions/search-middleware/lib/python3.9/site-packages/testfixtures/comparison.py", line 599, in different
    result = comparer(x, y, self)
  File "/Users/ludwigc/.pyenv/versions/search-middleware/lib/python3.9/site-packages/testfixtures/comparison.py", line 117, in compare_object
    return compare_simple(x, y, context)
  File "/Users/ludwigc/.pyenv/versions/search-middleware/lib/python3.9/site-packages/testfixtures/comparison.py", line 47, in compare_simple
    diff = _compare_mapping(x_attrs, y_attrs, context, x,
  File "/Users/ludwigc/.pyenv/versions/search-middleware/lib/python3.9/site-packages/testfixtures/comparison.py", line 229, in _compare_mapping
    x_keys = set(x.keys())
AttributeError: 'NoneType' object has no attribute 'keys'

Above function call testfixtures.compare(x, y) ends up in testfixtures.comparison.compare_simple(x, y, context).

Apparently, re.Pattern.__repr__ cuts off the pattern string after 199 characters. Therefore, repr(x) == repr(y) even though the two patterns differ in their last character. In consequence, testfixtures.comparison.compare_simple(x, y, context) tries to compare x and y by their attributes. However, x_attrs and y_attrs are both None (i.e., they are not mappings), and the call to _compare_mapping(x_attrs, y_attrs, context, x, 'attributes ', '.%s') fails.

Thanks, this is fixed in 6.18.3. I'm very sorry to hear you have such long regex patterns!

Thanks @cjw296 for the quick fix!

The regex appeared inside a parser object attribute created using Lark for a moderately complex grammar. It's generated, so no worries... :-)