Skip to content

difflib._check_types allows string inputs instead of sequences of strings as documented #115801

Closed
@lampwins

Description

@lampwins

Bug report

Bug description:

Both difflib.unified_diff and difflib.context_diff document that a and b input arguments are to be lists of strings. These functions perform argument type checking by way of difflib._check_types, however this function allows a and b to be direct str arguments. Technically this does not cause a failure in difflib.unified_diff (and I assume the same is true for context_diff but I have not tested it), however, for very large strings a and/or b, difflib.unified_diff is exponentially slower to calculate the diff, because the underlying SequenceMatcher is optimized to compare two lists of string, not two sequences of chars.

We can obviously see that the implementation of _check_types uses a seemingly naive type check of the first element in a and b which will pass for both the documented input of a list of strings, but also for a positive length string itself.

def _check_types(a, b, *args):
    # Checking types is weird, but the alternative is garbled output when
    # someone passes mixed bytes and str to {unified,context}_diff(). E.g.
    # without this check, passing filenames as bytes results in output like
    #   --- b'oldfile.txt'
    #   +++ b'newfile.txt'
    # because of how str.format() incorporates bytes objects.
    if a and not isinstance(a[0], str):
        raise TypeError('lines to compare must be str, not %s (%r)' %
                        (type(a[0]).__name__, a[0]))
    if b and not isinstance(b[0], str):
        raise TypeError('lines to compare must be str, not %s (%r)' %
                        (type(b[0]).__name__, b[0]))
    for arg in args:
        if not isinstance(arg, str):
            raise TypeError('all arguments must be str, not: %r' % (arg,))

This would be rather trivial to fix in _check_types but I want to first make sure that the documentation of a list of string is to be considered correct behavior?

CPython versions tested on:

3.8, 3.9, 3.10, 3.11, 3.12, 3.13, CPython main branch

Operating systems tested on:

Linux, macOS

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions