seperman/deepdiff

Can't compare dicts with both single and double quotes in keys

kammala opened this issue · 3 comments

Describe the bug
DeepDiff falls with an exception while comparing dicts with keys containing both single and double quotes.

To Reproduce

import deepdiff

deepdiff.DeepDiff({'''a'"a''': 1}, {'''a'"a''': 2})

Expected behavior
A comparison result is being returned.

OS, DeepDiff version and Python version (please complete the following information):

  • DeepDiff Version: 6.7.0

Additional context
The branch to handle both single and double quotes in stringify_element doesn't define result variable which is used later in return statement which causes exception (UnboundLocalError: local variable 'result' referenced before assignment) to be triggered.

I get an error on stringify_element when param is a byte string.
With the following change (in the first 2 lines) it works.

def stringify_element(param, quote_str=None):
    if isinstance(param, str):
        has_quote = "'" in param
        has_double_quote = '"' in param
    else:
        has_quote = b"'" in param
        has_double_quote = b'"' in param
    if has_quote and has_double_quote:
        new_param = []
        for char in param:
            if char in {'"', "'"}:
                new_param.append('\\')
            new_param.append(char)
        result = ''.join(new_param)
    elif has_quote:
        result = f'"{param}"'
    elif has_double_quote:
        result = f"'{param}'"
    else:
        result = param if quote_str is None else quote_str.format(param)
    return result

I am not so familiar with all the DeepDiff code, thus let me try just suggesting a correction that seems to work assuming one is using 'utf8' encoding in bytes and bytearray

def stringify_element(param, quote_str=None):
    if isinstance(param, (bytes, bytearray)):   ## handle bytes
        param = param.decode('utf8')            ## here
    has_quote = "'" in param
    has_double_quote = '"' in param
    if has_quote and has_double_quote:
        new_param = []
        for char in param:
            if char in {'"', "'"}:
                new_param.append('\\')
            new_param.append(char)
        result = ''.join(new_param)
    elif has_quote:
        result = f'"{param}"'
    elif has_double_quote:
        result = f"'{param}'"
    else:
        result = param if quote_str is None else quote_str.format(param)
    return result

Test:

B = {'Foo\'\"':42}
A = {b'Foo\'\"':42}
deepdiff.DeepDiff(A, B)
Out[8]: 
{'dictionary_item_added': [root[Foo\'\"]],
 'dictionary_item_removed': [root[Foo\'\"]]}