python / python/cpython

difflib adding an additional html column when making tables using difflib.HtmlDiff()

未关闭
#117,943 4 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

stdlib type-bug
主要语言
Python
星标
77.2k
派生
36k
PR 合并指标
PR 指标待抓取

描述

Bug report

Bug description:

I am working with the python library difflib and specifically the class HtmlDiff. For some reason, the function make_table is adding a blank column in when generating an HTML table, throwing off difference highlights and defeating the entire purpose of the diff function alltogether.

Input python function:

# diff_tool.py

import argparse
import difflib
import sys

from pathlib import Path


def create_diff(output_file: Path = None):
        
    # short list of strings to compare, nonproblematic (see expected output)
    thing1 = ["this", "that"]
    thing2 = ["thiis", "that2"]

    # longer list of strings to compare, mirroring the problematic data (see actual output)
    file_2 = ['Ab', 
    'my_id: ID1234, attribute: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f , name: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f ', 
    'my_id: ID5678, attribute: abcde ghijklm opyrst vwxyz bc efghij lmnopyr tuv xyzab defabcde ghijklmno yrstuvwxyza cdefghijklmnop rstuvw yz b defab defgh jklmnop rstuvwxyz bcd fghijkl nopyr tuvwxyza cdefabcd fghijkl no yrs uvwxyzabcd fghij l n, name: ab defghi klmnopy stuvwxy abcde ghijklm op']
                   
    file_2_new = ['Abcdefghijklmn', 
    'my_id: ID1234, attribute: abcde ghijklm opyrst vwxyz bc efghij lmnopyr tuv xyzab defabcde ghijklmno yrstuvwxyza cdefghijklmnop rstuvw yz b defab defgh jklmnop rstuvwxyz bcd fghijkl nopyr tuvwxyza cdefabcd fghijkl no yrs uvwxyzabcd fghij l n, name: ab defghi klmnopy stuvwxy abcde ghijklm op', 
    'my_id: ID5678, attribute: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f , name: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f ']

    # if output HTML file option at command line
    if output_file:
    
        # create HtmlDiff Object
        my_html = difflib.HtmlDiff()
        
        # make table with problematic data, delta1 is a string
        delta1 = my_html.make_table(
            file_2, file_2_new, "table1A" , "table1B"
        )     

        # replace nowrap tags and weird space characters
        delta1 = delta1.replace(" ", " ")
        delta1 = delta1.replace("nowrap=\"nowrap\"", "")

        # html header which goes at beggining of html file
        html_header = '''

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
<body style="background-color:white;">
<head>
    <meta http-equiv="Content-Type"
          content="text/html; charset=utf-8" />
    <title></title>
    <style type="text/css">
        table.diff {font-family:Courier; color: black; border:medium;table-layout: auto; width: 96%; word-wrap: break-word; margin-left: auto; margin-right: auto;}
        td.diff_header {text-align:left}
        .diff_header {background-color:#e0e0e0}
        .diff_next {background-color:#c0c0c0}
        .diff_add {background-color:#aaffaa}
        .diff_chg {background-color:#ffff77}
        .diff_sub {background-color:#ffaaaa}
        .widthA{width:1%}
        .widthB{width:7%}
        .widthC{width:40%}
        .element_title{color: black; text-align:center}
        .element_diff{color: black; width: 96%; margin-left: auto; margin-right: auto}
    </style>
</head>

'''
        # html footer which goes at the end of html file
        html_footer = '''
        </body>

</html>
        '''
        
        # open output file path and write header, table and footer
        with open(output_file, "w") as f:
            f.write(html_header)
            f.write(delta1)
            f.write(html_footer)
    

def main():

    # parse command line option for output HTML file
    parser = argparse.ArgumentParser()
    parser.add_argument("--html", help="specify html to write to")
    args = parser.parse_args()


    if args.html:
        output_file = Path(args.html)
    else:
        output_file = None

    # call create_diff function which compares two lists of strings
    # and returns a formatted HTML table comparing them
    create_diff(output_file)


if __name__ == "__main__":
    main()

Output HTML file with problematic data:



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
<body style="background-color:white;">
<head>
    <meta http-equiv="Content-Type"
          content="text/html; charset=utf-8" />
    <title></title>
    <style type="text/css">
        table.diff {font-family:Courier; color: black; border:medium;table-layout: auto; width: 96%; word-wrap: break-word; margin-left: auto; margin-right: auto;}
        td.diff_header {text-align:left}
        .diff_header {background-color:#e0e0e0}
        .diff_next {background-color:#c0c0c0}
        .diff_add {background-color:#aaffaa}
        .diff_chg {background-color:#ffff77}
        .diff_sub {background-color:#ffaaaa}
        .widthA{width:1%}
        .widthB{width:7%}
        .widthC{width:40%}
        .element_title{color: black; text-align:center}
        .element_diff{color: black; width: 96%; margin-left: auto; margin-right: auto}
    </style>
</head>


    <table class="diff" id="difflib_chg_to0__top"
           cellspacing="0" cellpadding="0" rules="groups" >
        <colgroup></colgroup> <colgroup></colgroup> <colgroup></colgroup>
        <colgroup></colgroup> <colgroup></colgroup> <colgroup></colgroup>
        <thead><tr><th class="diff_next"><br /></th><th colspan="2" class="diff_header">table1A</th><th class="diff_next"><br /></th><th colspan="2" class="diff_header">table1B</th></tr></thead>
        <tbody>
            <tr><td class="diff_next" id="difflib_chg_to0__0"><a href="#difflib_chg_to0__top">t</a></td><td class="diff_header" id="from0_1">1</td><td ><span class="diff_sub">Ab</span></td><td class="diff_next"><a href="#difflib_chg_to0__top">t</a></td><td class="diff_header" id="to0_1">1</td><td ><span class="diff_add">Abcdefghijklmn</span></td></tr>
            <tr><td class="diff_next"></td><td class="diff_header" id="from0_2">2</td><td ><span class="diff_sub">my_id: ID1234, attribute: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f , name: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f </span></td><td class="diff_next"></td><td class="diff_header"></td><td ></td></tr>
            <tr><td class="diff_next"></td><td class="diff_header" id="from0_3">3</td><td >my_id: ID<span class="diff_chg">5678</span>, attribute: abcde ghijklm opyrst vwxyz bc efghij lmnopyr tuv xyzab defabcde ghijklmno yrstuvwxyza cdefghijklmnop rstuvw yz b defab defgh jklmnop rstuvwxyz bcd fghijkl nopyr tuvwxyza cdefabcd fghijkl no yrs uvwxyzabcd fghij l n, name: ab defghi klmnopy stuvwxy abcde ghijklm op</td><td class="diff_next"></td><td class="diff_header" id="to0_2">2</td><td >my_id: ID<span class="diff_chg">1234</span>, attribute: abcde ghijklm opyrst vwxyz bc efghij lmnopyr tuv xyzab defabcde ghijklmno yrstuvwxyza cdefghijklmnop rstuvw yz b defab defgh jklmnop rstuvwxyz bcd fghijkl nopyr tuvwxyza cdefabcd fghijkl no yrs uvwxyzabcd fghij l n, name: ab defghi klmnopy stuvwxy abcde ghijklm op</td></tr>
            <tr><td class="diff_next"></td><td class="diff_header"></td><td ></td><td class="diff_next"></td><td class="diff_header" id="to0_3">3</td><td ><span class="diff_add">my_id: ID5678, attribute: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f , name: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f </span></td></tr>
        </tbody>
    </table>
        </body>

</html>
        

The problematic line in the HTML is:

<tr><td class="diff_next"></td><td class="diff_header" id="from0_2">2</td><td ><span class="diff_sub">my_id: ID1234, attribute: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f , name: abcd fghijklmn py stuvwx zabc efg ijklmnopy st vwxy abcd f </span></td><td class="diff_next"></td><td class="diff_header"></td><td ></td></tr>

Where there is an extra <td ></td> at the end, adding an extra column.

This is what the table looks like with the unwanted column

This is what the table is supposed to look like when the columns are aligned correctly

I have tried changing the length of the string, checking for invisible characters, removing colons. There is something wrong with the strings that I am providing as input which throws off the make_table function. I have provided make_table with longer strings and the output was just fine. The behavior is very inconsistent.

Thanks for the help.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

使用提供的 file_2 和 file_2_new 列表,从 diff_tool.py 中的复现和 difflib.HtmlDiff().make_table 入口点开始。检查生成的行和列是如何组装的,然后验证有问题的输入不再产生额外的空单元格,并为复现的情况添加回归覆盖。

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
tooling
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
停滞
描述清晰度
基本清楚
新手友好度
42/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。