| ����?��^
|
|
����?��^
|
| ����+�ore
|
|
����+�ore
|
| ����?��^
|
|
����?��^
|
| ����-�two
|
|
����-�two
|
| ����-�three
|
|
����-�three
|
| ����?��-
|
|
����?��-
|
| ����+�tree
|
|
����+�tree
|
| ����+�emu
|
|
����+�emu
|
| ����"""
|
|
����"""
|
| ����return�Differ(linejunk,�charjunk).compare(a,�b)
|
|
����return�Differ(linejunk,�charjunk).compare(a,�b)
|
|
|
t |
�
|
|
|
|
def�mdiff(fromlines,�tolines,�chgfmt,�linefmt,�context=None,�sep=None,�
|
|
|
|
����������linejunk=None,�charjunk=IS_CHARACTER_JUNK):
|
|
|
|
����"""Returns�generator�yielding�marked�up�from/to�side�by�side�difference�lines.
|
|
|
|
�
|
|
|
|
����Arguments:
|
|
|
|
����fromlines�--�text�lines�which�will�be�iterated�over�and�compared�to�tolines
|
|
|
|
����tolines�--�text�lines�which�will�be�iterated�over�and�compared�to�fromlines
|
|
|
|
����chgfmt�--�function�to�markup�add/delete/change�differences�in�text�lines
|
|
|
|
��������������(see�example�below)
|
|
|
|
����linefmt�--�function�to�format�line�of�text�for�display�(see�example�below)
|
|
|
|
����context�--�number�of�context�lines�to�display�on�each�side�of�difference,
|
|
|
|
���������������if�None�or�less�that�1,�the�all�from/to�text�lines�will�be
|
|
|
|
���������������generated.
|
|
|
|
����sep�--�separator�string�to�use�between�context�differences.
|
|
|
|
����linejunk�--�passed�on�to�ndiff�(see�ndiff�documentation)
|
|
|
|
����charjunk�--�passed�on�to�ndiff�(see�ndiff�documentation)
|
|
|
|
����
|
|
|
|
����This�function�returns�an�interator�which�returns�a�tuple�of�a�"from"
|
|
|
|
����line,�a�corresponding�"to"�line�and�a�boolean�indicating�if�either�the
|
|
|
|
����"from"�or�"to"�line�contains�a�difference.
|
|
|
|
�
|
|
|
|
����This�function/iterator�was�originally�developed�to�generate�side�by�side
|
|
|
|
����file�difference�for�making�HTML�pages.��The�function�requires�functions�to
|
|
|
|
����be�passed�in�as�arguments�to�allow�it�to�be�configurable�to�generate�any
|
|
|
|
����type�of�markup�such�as�HTML�or�XHTML.��The�function�supports�generating�a
|
|
|
|
����full�file�difference�report�or�just�contextual�differences.
|
|
|
|
�
|
|
|
|
����See�HtmlDiff�class�for�an�example�usage�of�this�function.��Note,�this
|
|
|
|
����function�utilizes�the�ndiff�function�to�generate�the�side�by�side
|
|
|
|
����difference�markup.��Optional�ndiff�arguments�may�be�passed�to�this�function
|
|
|
|
����and�they�in�turn�will�be�passed�to�ndiff.����
|
|
|
|
����"""
|
|
|
|
����import�re�
|
|
|
|
�
|
|
|
|
����#�adjust�number�of�context�lines�to�include�the�line�with�the�change
|
|
|
|
����if�context:
|
|
|
|
��������context�+=�1
|
|
|
|
�
|
|
|
|
����#�regular�expression�for�finding�intraline�change�indices
|
|
|
|
����change_re�=�re.compile('(\++|\-+|\^+)')
|
|
|
|
����
|
|
|
|
����#�regular�expression�to�find�hidden�markers
|
|
|
|
����marker_re�=�re.compile('\0([+-^])(.*?)\1',re.DOTALL)
|
|
|
|
�
|
|
|
|
����#�create�the�difference�iterator�to�generate�the�differences
|
|
|
|
����diff_lines_iterator�=�ndiff(fromlines,tolines,linejunk,charjunk)
|
|
|
|
�
|
|
|
|
����def�_make_line(lines,�format_key,�side,�num_lines=[0,0]):
|
|
|
|
��������"""Returns�line�of�text�with�user's�change�markup�and�line�formatting.
|
|
|
|
�
|
|
|
|
��������lines�--�list�of�lines�from�the�ndiff�generator�to�produce�a�line�of
|
|
|
|
�����������������text�from.��When�producing�the�line�of�text�to�return,�the
|
|
|
|
�����������������lines�used�are�removed�from�this�list.
|
|
|
|
��������format_key�--�'+'�return�first�line�in�list�with�"add"�markup�around
|
|
|
|
�������������������������the�entire�line.
|
|
|
|
���������������������'-'�return�first�line�in�list�with�"delete"�markup�around
|
|
|
|
�������������������������the�entire�line.
|
|
|
|
���������������������'?'�return�first�line�in�list�with�add/delete/change
|
|
|
|
�������������������������intraline�markup�(indices�obtained�from�second�line)
|
|
|
|
���������������������None�return�first�line�in�list�with�no�markup
|
|
|
|
��������side�--�indice�into�the�num_lines�list�(0=from,1=to)
|
|
|
|
��������num_lines�--�from/to�current�line�number.��This�is�NOT�intended�to�be�a
|
|
|
|
���������������������passed�parameter.��It�is�present�as�a�keyword�argument�to
|
|
|
|
���������������������maintain�memory�of�the�current�line�numbers�between�calls
|
|
|
|
���������������������of�this�function.
|
|
|
|
�
|
|
|
|
��������Note,�this�function�is�purposefully�not�defined�at�the�module�scope�so
|
|
|
|
��������that�data�it�needs�from�its�parent�function�(within�whose�context�it
|
|
|
|
��������is�defined)�does�not�need�to�be�of�module�scope.
|
|
|
|
��������"""
|
|
|
|
��������num_lines[side]�+=�1
|
|
|
|
��������#�Handle�case�where�no�user�markup�is�to�be�added,�just�return�line�of
|
|
|
|
��������#�text�with�user's�line�format�to�allow�for�usage�of�the�line�number.
|
|
|
|
��������if�format_key�is�None:
|
|
|
|
������������return�linefmt(side,num_lines[side],lines.pop(0)[2:])
|
|
|
|
��������#�Handle�case�of�intraline�changes
|
|
|
|
��������if�format_key�==�'?':
|
|
|
|
������������text,�markers�=�lines.pop(0),�lines.pop(0)
|
|
|
|
������������#�find�intraline�changes�(store�change�type�and�indices�in�tuples)
|
|
|
|
������������sub_info�=�[]
|
|
|
|
������������def�record_sub_info(match_object,sub_info=sub_info):
|
|
|
|
����������������sub_info.append([match_object.group(1)[0],match_object.span()])
|
|
|
|
����������������return�match_object.group(1)
|
|
|
|
������������change_re.sub(record_sub_info,markers)
|
|
|
|
������������#�process�each�tuple�inserting�our�special�marks�that�won't�be
|
|
|
|
������������#�noticed�by�an�xml/html�escaper.
|
|
|
|
������������for�key,(begin,end)�in�sub_info[::-1]:
|
|
|
|
����������������text�=�text[0:begin]+'\0'+key+text[begin:end]+'\1'+text[end:]
|
|
|
|
������������text�=�text[2:]
|
|
|
|
��������#�Handle�case�of�add/delete�entire�line
|
|
|
|
��������else:
|
|
|
|
������������text�=�lines.pop(0)[2:]
|
|
|
|
������������#�if�line�of�text�is�just�a�newline,�insert�a�space�so�there�is
|
|
|
|
������������#�something�for�the�user�to�highlight�and�see.
|
|
|
|
������������if�len(text)�<=�1:
|
|
|
|
����������������text�=�'�'+text
|
|
|
|
������������#�insert�marks�that�won't�be�noticed�by�an�xml/html�escaper.
|
|
|
|
������������text�=�'\0'�+�format_key�+�text�+�'\1'
|
|
|
|
��������#�Return�line�of�text,�first�allow�user's�line�formatter�to�do�it's
|
|
|
|
��������#�thing�(such�as�adding�the�line�number)�then�replace�the�special
|
|
|
|
��������#�marks�with�what�the�user's�change�markup.
|
|
|
|
��������line_num�=�num_lines[side]
|
|
|
|
��������replacer�=�lambda�m�:�chgfmt(side,line_num,m.group(2),m.group(1))
|
|
|
|
��������return�marker_re.sub(replacer,linefmt(side,line_num,text))
|
|
|
|
����
|
|
|
|
����def�_line_iterator():
|
|
|
|
��������"""Yields�from/to�lines�of�text�with�a�change�indication.
|
|
|
|
�
|
|
|
|
��������This�function�is�an�iterator.��It�itself�pulls�lines�from�a
|
|
|
|
��������differencing�iterator,�processes�them�and�yields�them.��When�it�can
|
|
|
|
��������it�yields�both�a�"from"�and�a�"to"�line,�otherwise�it�will�yield�one
|
|
|
|
��������or�the�other.��Processing�includes�formatting�the�line�with�the�user's
|
|
|
|
��������line�formatter�(for�adding�line�numbering)�and�formatting�differences
|
|
|
|
��������using�the�user's�change�format�function.��In�addition�to�yielding�the
|
|
|
|
��������lines�of�from/to�text,�a�boolean�flag�is�yielded�to�indicate�if�the
|
|
|
|
��������text�line(s)�have�differences�in�them.
|
|
|
|
�
|
|
|
|
��������Note,�this�function�is�purposefully�not�defined�at�the�module�scope�so
|
|
|
|
��������that�data�it�needs�from�its�parent�function�(within�whose�context�it
|
|
|
|
��������is�defined)�does�not�need�to�be�of�module�scope.
|
|
|
|
��������"""
|
|
|
|
��������lines�=�[]
|
|
|
|
��������num_blanks_pending,�num_blanks_to_yield�=�0,�0
|
|
|
|
��������while�True:��������
|
|
|
|
������������#�Load�up�next�4�lines�so�we�can�look�ahead,�create�strings�which
|
|
|
|
������������#�are�a�concatenation�of�the�first�character�of�each�of�the�4�lines
|
|
|
|
������������#�so�we�can�do�some�very�readable�comparisons.
|
|
|
|
������������while�len(lines)�<�4:
|
|
|
|
����������������try:
|
|
|
|
��������������������lines.append(diff_lines_iterator.next())
|
|
|
|
����������������except�StopIteration:
|
|
|
|
��������������������lines.append('X')
|
|
|
|
������������s�=�''.join([line[0]�for�line�in�lines])
|
|
|
|
������������if�s.startswith('X'):
|
|
|
|
����������������#�When�no�more�lines,�pump�out�any�remaining�blank�lines�so�the
|
|
|
|
����������������#�corresponding�add/delete�lines�get�a�matching�blank�line�so
|
|
|
|
����������������#�all�line�pairs�get�yielded�at�the�next�level.
|
|
|
|
����������������num_blanks_to_yield�=�num_blanks_pending
|
|
|
|
������������elif�s.startswith('-?+?'):
|
|
|
|
����������������#�simple�intraline�change
|
|
|
|
����������������yield�_make_line(lines,'?',0),�_make_line(lines,'?',1),�True
|
|
|
|
����������������continue
|
|
|
|
������������elif�s.startswith('--++'):
|
|
|
|
����������������#�in�delete�block,�add�block�coming:�we�do�NOT�want�to�get
|
|
|
|
����������������#�caught�up�on�blank�lines�yet,�just�process�the�delete�line
|
|
|
|
����������������num_blanks_pending�-=�1
|
|
|
|
����������������yield�_make_line(lines,'-',0),�None,�True
|
|
|
|
����������������continue
|
|
|
|
������������elif�s.startswith('--?+')�or�s.startswith('--+')�or�\
|
|
|
|
�����������������s.startswith('-�'):
|
|
|
|
����������������#�in�delete�block�and�see�a�intraline�change�or�unchanged�line
|
|
|
|
����������������#�coming:�yield�the�delete�line�and�then�blanks
|
|
|
|
����������������from_line,to_line�=�_make_line(lines,'-',0),�None
|
|
|
|
����������������num_blanks_to_yield,num_blanks_pending�=�num_blanks_pending-1,0
|
|
|
|
������������elif�s.startswith('-+?'):
|
|
|
|
����������������#�intraline�change
|
|
|
|
����������������yield�_make_line(lines,None,0),�_make_line(lines,'?',1),�True
|
|
|
|
����������������continue
|
|
|
|
������������elif�s.startswith('-?+'):
|
|
|
|
����������������#�intraline�change
|
|
|
|
����������������yield�_make_line(lines,'?',0),�_make_line(lines,None,1),�True
|
|
|
|
����������������continue
|
|
|
|
������������elif�s.startswith('-'):
|
|
|
|
����������������#�delete�FROM�line
|
|
|
|
����������������num_blanks_pending�-=�1
|
|
|
|
����������������yield�_make_line(lines,'-',0),�None,�True
|
|
|
|
����������������continue
|
|
|
|
������������elif�s.startswith('+--'):
|
|
|
|
����������������#�in�add�block,�delete�block�coming:�we�do�NOT�want�to�get
|
|
|
|
����������������#�caught�up�on�blank�lines�yet,�just�process�the�add�line
|
|
|
|
����������������num_blanks_pending�+=�1
|
|
|
|
����������������yield�None,�_make_line(lines,'+',1),�True
|
|
|
|
����������������continue
|
|
|
|
������������elif�s.startswith('+�')�or�s.startswith('+-'):
|
|
|
|
����������������#�will�be�leaving�an�add�block:�yield�blanks�then�add�line
|
|
|
|
����������������from_line,�to_line�=�None,�_make_line(lines,'+',1)
|
|
|
|
����������������num_blanks_to_yield,num_blanks_pending�=�num_blanks_pending+1,0
|
|
|
|
������������elif�s.startswith('+'):
|
|
|
|
����������������#�inside�an�add�block,�yield�the�add�line
|
|
|
|
����������������num_blanks_pending�+=�1
|
|
|
|
����������������yield�None,�_make_line(lines,'+',1),�True
|
|
|
|
����������������continue
|
|
|
|
������������elif�s.startswith('�'):
|
|
|
|
����������������#�unchanged�text,�yield�it�to�both�sides
|
|
|
|
����������������yield�_make_line(lines[:],None,0),_make_line(lines,None,1),False
|
|
|
|
����������������continue
|
|
|
|
������������#�Catch�up�on�the�blank�lines�so�when�we�yield�the�next�from/to
|
|
|
|
������������#�pair,�they�are�lined�up.
|
|
|
|
������������while(num_blanks_to_yield�<�0):
|
|
|
|
����������������num_blanks_to_yield�+=�1
|
|
|
|
����������������yield�None,linefmt(1,None,'\n'),True
|
|
|
|
������������while(num_blanks_to_yield�>�0):
|
|
|
|
����������������num_blanks_to_yield�-=�1
|
|
|
|
����������������yield�linefmt(0,None,'\n'),None,True
|
|
|
|
������������if�s.startswith('X'):
|
|
|
|
����������������raise�StopIteration
|
|
|
|
������������else:
|
|
|
|
����������������yield�from_line,to_line,True
|
|
|
|
�
|
|
|
|
����def�_line_pair_iterator():
|
|
|
|
��������"""Yields�from/to�lines�of�text�with�a�change�indication.
|
|
|
|
�
|
|
|
|
��������This�function�is�an�iterator.��It�itself�pulls�lines�from�the�line
|
|
|
|
��������iterator.��It's�difference�from�that�iterator�is�that�this�function
|
|
|
|
��������always�yields�a�pair�of�from/to�text�lines�(with�the�change
|
|
|
|
��������indication).��If�necessary�it�will�collect�single�from/to�lines
|
|
|
|
��������until�it�has�a�matching�pair�from/to�pair�to�yield.
|
|
|
|
�
|
|
|
|
��������Note,�this�function�is�purposefully�not�defined�at�the�module�scope�so
|
|
|
|
��������that�data�it�needs�from�its�parent�function�(within�whose�context�it
|
|
|
|
��������is�defined)�does�not�need�to�be�of�module�scope.
|
|
|
|
��������"""
|
|
|
|
��������line_iterator�=�_line_iterator()
|
|
|
|
��������fromlines,tolines=[],[]
|
|
|
|
��������while�True:
|
|
|
|
������������#�Collecting�lines�of�text�until�we�have�a�from/to�pair
|
|
|
|
������������while�(len(fromlines)==0�or�len(tolines)==0):
|
|
|
|
����������������from_line,�to_line,�found_diff�=line_iterator.next()
|
|
|
|
����������������if�from_line�is�not�None:
|
|
|
|
��������������������fromlines.append((from_line,found_diff))
|
|
|
|
����������������if�to_line�is�not�None:
|
|
|
|
��������������������tolines.append((to_line,found_diff))
|
|
|
|
������������#�Once�we�have�a�pair,�remove�them�from�the�collection�and�yield�it
|
|
|
|
������������from_line,�fromDiff�=�fromlines.pop(0)
|
|
|
|
������������to_line,�to_diff�=�tolines.pop(0)
|
|
|
|
������������yield�(from_line,to_line,fromDiff�or�to_diff)
|
|
|
|
�
|
|
|
|
����#�Handle�case�where�user�does�not�want�context�differencing,�just�yield
|
|
|
|
����#�them�up�without�doing�anything�else�with�them.
|
|
|
|
����line_pair_iterator�=�_line_pair_iterator()
|
|
|
|
����if�context�is�None�or�context�<=�0:
|
|
|
|
��������while�True:
|
|
|
|
������������yield�line_pair_iterator.next()
|
|
|
|
����#�Handle�case�where�user�wants�context�differencing.��We�must�do�some
|
|
|
|
����#�storage�of�lines�until�we�know�for�sure�that�they�are�to�be�yielded.
|
|
|
|
����else:
|
|
|
|
��������lines_to_write�=�0
|
|
|
|
��������insert_separator�=�False
|
|
|
|
��������while�True:
|
|
|
|
������������#�Store�lines�up�until�we�find�a�difference,�note�use�of�a
|
|
|
|
������������#�circular�queue�because�we�only�need�to�keep�around�what
|
|
|
|
������������#�we�need�for�context.
|
|
|
|
������������index,�contextLines�=�0,�[None]*(context)
|
|
|
|
������������found_diff�=�False
|
|
|
|
������������while(found_diff�is�False):
|
|
|
|
����������������from_line,�to_line,�found_diff�=�line_pair_iterator.next()
|
|
|
|
����������������i�=�index�%�context
|
|
|
|
����������������contextLines[i]�=�(from_line,�to_line,�found_diff)
|
|
|
|
����������������index�+=�1
|
|
|
|
������������#�Yield�lines�that�we�have�collected�so�far,�but�first�yield
|
|
|
|
������������#�the�user's�separator.
|
|
|
|
������������if�insert_separator:
|
|
|
|
����������������yield�sep,�sep,�None
|
|
|
|
������������else:
|
|
|
|
����������������insert_separator�=�True
|
|
|
|
������������if�index�>�context:
|
|
|
|
����������������lines_to_write�=�context
|
|
|
|
������������else:
|
|
|
|
����������������lines_to_write�=�index
|
|
|
|
����������������index�=�0
|
|
|
|
������������while(lines_to_write):
|
|
|
|
����������������i�=�index�%�context
|
|
|
|
����������������index�+=�1
|
|
|
|
����������������yield�contextLines[i]
|
|
|
|
����������������lines_to_write�-=�1
|
|
|
|
������������#�Now�yield�the�context�lines�after�the�change
|
|
|
|
������������lines_to_write�=�context-1
|
|
|
|
������������while(lines_to_write):
|
|
|
|
����������������from_line,�to_line,�found_diff�=�line_pair_iterator.next()
|
|
|
|
����������������#�If�another�change�within�the�context,�extend�the�context
|
|
|
|
����������������if�found_diff:
|
|
|
|
��������������������lines_to_write�=�context
|
|
|
|
����������������else:
|
|
|
|
��������������������lines_to_write�-=�1
|
|
|
|
����������������yield�from_line,�to_line,�found_diff
|
|
|
|
�
|
|
|
|
�
|
|
|
|
class�HtmlDiff(object):
|
|
|
|
����"""For�producing�HTML�side�by�side�comparison�with�change�highlights.
|
|
|
|
�
|
|
|
|
����This�class�can�be�used�to�create�an�HTML�table�(or�a�complete�HTML�file
|
|
|
|
����containing�the�table)�showing�a�side�by�side,�line�by�line�comparision
|
|
|
|
����of�text�with�inter-line�and�intra-line�change�highlights.��The�table�can�
|
|
|
|
����be�generated�in�either�full�or�contextual�difference�mode.��Additional�
|
|
|
|
����control�of�the�format�of�the�generated�difference�table�can�be�controlled�
|
|
|
|
����by�subclassing�and�overriding�the�appropriate�template�or�method.
|
|
|
|
�
|
|
|
|
����The�following�templates�and�methods�are�intended�for�subclass�overriding:
|
|
|
|
�
|
|
|
|
����file_template�--�controls�HTML�file�format
|
|
|
|
����styles�--�style�specifications�for�change�highlights
|
|
|
|
����table_template�--�controls�difference�table�format
|
|
|
|
����linenum_template�--�controls�format�of�line�number�column
|
|
|
|
����legend�--�legend�table�content
|
|
|
|
�
|
|
|
|
����format_line�--�method�to�markup�each�line
|
|
|
|
����format_change�--�method�to�provide�change�highlight�markup
|
|
|
|
�
|
|
|
|
����The�following�methods�are�provided�for�HTML�generation:
|
|
|
|
�
|
|
|
|
����make_table�--�generates�HTML�for�a�single�side�by�side�table
|
|
|
|
����make_file�--�generates�complete�HTML�file�with�a�single�side�by�side�table
|
|
|
|
�
|
|
|
|
����See�tools/scripts/diff.py�for�an�example�usage�of�this�class.����
|
|
|
|
����"""
|
|
|
|
�
|
|
|
|
����file_template�=�"""
|
|
|
|
��������<!DOCTYPE�html�PUBLIC�"-//W3C//DTD�XHTML�1.0�Transitional//EN"
|
|
|
|
������������"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
|
|
��������<html>
|
|
|
|
��������<head>
|
|
|
|
��������<meta�http-equiv="Content-Type"�
|
|
|
|
��������������content="text/html;�charset=ISO-8859-1"�/>
|
|
|
|
��������<title>%(title)s</title>
|
|
|
|
��������<style�type="text/css">%(styles)s
|
|
|
|
��������</style>
|
|
|
|
��������</head>
|
|
|
|
��������<body>
|
|
|
|
��������%(header)s
|
|
|
|
��������%(table)s%(legend)s
|
|
|
|
��������</body>
|
|
|
|
��������</html>"""
|
|
|
|
�
|
|
|
|
����styles�=�"""
|
|
|
|
��������.diff_header�{background-color:#e0e0e0}
|
|
|
|
��������td.diff_header�{text-align:right}
|
|
|
|
��������.diff_next�{background-color:#c0c0c0}
|
|
|
|
��������.diff_add�{background-color:#aaffaa}
|
|
|
|
��������.diff_chg�{background-color:#ffff77}
|
|
|
|
��������.diff_sub�{background-color:#ffaaaa}"""
|
|
|
|
�
|
|
|
|
����linenum_template�=�"%d"
|
|
|
|
�
|
|
|
|
����table_template�=�"""
|
|
|
|
��������<table�summary="%(summary)s"�id="difflib_chg_%(prefix)s_top"�border="3"�
|
|
|
|
���������������cellspacing="0"�cellpadding="0"�rules="groups"�
|
|
|
|
���������������style="font-family:Courier">
|
|
|
|
������������<colgroup></colgroup>�<colgroup></colgroup>�<colgroup></colgroup>�
|
|
|
|
������������<colgroup></colgroup>�<colgroup></colgroup>
|
|
|
|
������������%(header_row)s<tbody>%(data_rows)s</tbody>
|
|
|
|
��������</table>"""
|
|
|
|
�
|
|
|
|
����legend�=�"""
|
|
|
|
��������<table�summary="Legends"�style="font-family:Courier">
|
|
|
|
������������<tr>�<th�colspan="2">�Legends�</th>�</tr>
|
|
|
|
������������<tr>�<td>�<table�border=""�summary="Colors">
|
|
|
|
��������������������������<tr><th>�Colors�</th>�</tr>
|
|
|
|
��������������������������<tr><td�class="diff_add"> Added </td></tr>
|
|
|
|
��������������������������<tr><td�class="diff_chg">Changed</td>�</tr>
|
|
|
|
��������������������������<tr><td�class="diff_sub">Deleted</td>�</tr>
|
|
|
|
����������������������</table></td>
|
|
|
|
�����������������<td>�<table�border=""�summary="Links">
|
|
|
|
��������������������������<tr><th�colspan="2">�Links�</th>�</tr>
|
|
|
|
��������������������������<tr><td>(f)irst�change</td>�</tr>
|
|
|
|
��������������������������<tr><td>(n)ext�change</td>�</tr>
|
|
|
|
��������������������������<tr><td>(t)op</td>�</tr>
|
|
|
|
����������������������</table></td>�</tr>
|
|
|
|
��������</table>"""
|
|
|
|
�
|
|
|
|
����def�__init__(self,prefix=['from','to'],�linejunk=None,�
|
|
|
|
�����������������charjunk=IS_CHARACTER_JUNK):
|
|
|
|
��������"""HtmlDiff�instance�initializer
|
|
|
|
�
|
|
|
|
��������Arguments:
|
|
|
|
�
|
|
|
|
��������prefix�--�from/to�line�anchors�name�prefix�(useful�for�hyperlinking�to
|
|
|
|
������������specific�lines�in�a�table�when�multiple�tables�exist�so�that�the
|
|
|
|
������������correct�table�can�be�referenced)
|
|
|
|
��������linejunk�--�passed�on�to�ndiff�(see�ndiff�documentation)
|
|
|
|
��������charjunk�--�passed�on�to�ndiff�(see�ndiff�documentation)
|
|
|
|
��������"""
|
|
|
|
��������self._prefix�=�prefix
|
|
|
|
��������self._linejunk�=�linejunk
|
|
|
|
��������self._charjunk�=�charjunk
|
|
|
|
��������import�xml.sax.saxutils�
|
|
|
|
��������self._escape�=�xml.sax.saxutils.escape
|
|
|
|
�
|
|
|
|
����def�make_file(self,fromlines,tolines,fromdesc='',todesc='',context=False,
|
|
|
|
������������������numlines=5,title='',header='',summary=''):
|
|
|
|
��������"""Returns�HTML�file�of�side�by�side�comparison�with�change�highlights
|
|
|
|
�
|
|
|
|
��������Arguments:
|
|
|
|
�
|
|
|
|
��������fromlines�--�list�of�"from"�lines
|
|
|
|
��������tolines�--�list�of�"to"�lines
|
|
|
|
��������fromdesc�--�"from"�file�column�header�string
|
|
|
|
��������todesc�--�"to"�file�column�header�string
|
|
|
|
��������context�--�set�to�True�for�contextual�differences
|
|
|
|
��������numlines�--�number�of�context�lines�(needed�for�full�differences�to
|
|
|
|
������������place�the�"next"�anchor�a�few�lines�ahead�of�the�next�change)
|
|
|
|
��������title�--�window�title�string
|
|
|
|
��������header�--�header�HTML�string�to�be�placed�above�table
|
|
|
|
��������summary�--�summary�attribute�of�table�string
|
|
|
|
��������"""
|
|
|
|
����������������
|
|
|
|
��������return�self.file_template�%�dict(
|
|
|
|
������������styles�=�self.styles,
|
|
|
|
������������legend�=�self.legend,
|
|
|
|
������������title�=�title,
|
|
|
|
������������header�=�header,
|
|
|
|
������������table�=�self.make_table(fromlines,tolines,fromdesc,todesc,
|
|
|
|
������������������������������������context=context,numlines=numlines,
|
|
|
|
������������������������������������summary=summary))
|
|
|
|
����
|
|
|
|
����def�make_table(self,fromlines,tolines,fromdesc='',todesc='',context=False,
|
|
|
|
�������������������numlines=5,summary=''):
|
|
|
|
��������"""Returns�HTML�table�of�side�by�side�comparison�with�change�highlights
|
|
|
|
�
|
|
|
|
��������Arguments:
|
|
|
|
��������fromlines�--�list�of�"from"�lines
|
|
|
|
��������tolines�--�list�of�"to"�lines
|
|
|
|
��������fromdesc�--�"from"�file�column�header�string
|
|
|
|
��������todesc�--�"to"�file�column�header�string
|
|
|
|
��������context�--�set�to�True�for�contextual�differences
|
|
|
|
��������numlines�--�number�of�context�lines�(needed�for�full�differences�to
|
|
|
|
������������place�the�"next"�anchor�a�few�lines�ahead�of�the�next�change)
|
|
|
|
��������summary�--�summary�attribute�of�table
|
|
|
|
��������"""
|
|
|
|
��������
|
|
|
|
��������if�context:
|
|
|
|
������������context�=�numlines
|
|
|
|
��������else:
|
|
|
|
������������context�=�0
|
|
|
|
��������prefix�=�self._prefix[1]
|
|
|
|
��������#�collect�up�from/to�lines�in�string,�difference�flags�in�a�list
|
|
|
|
��������from_text,�to_text,�diff_flags�=�[],[],[]
|
|
|
|
��������diffs�=�mdiff(fromlines,tolines,self.format_change,self.format_line,
|
|
|
|
����������������������context,None,linejunk=self._linejunk,
|
|
|
|
����������������������charjunk=self._charjunk)
|
|
|
|
��������for�from_line,�to_line,�found_diff�in�diffs:
|
|
|
|
������������from_text.append(from_line)
|
|
|
|
������������to_text.append(to_line)
|
|
|
|
������������diff_flags.append(found_diff)
|
|
|
|
��������#�process�change�flags,�generating�middle�column�of�next�anchors/links
|
|
|
|
��������next_id�=�['']*len(diff_flags)
|
|
|
|
��������next_href�=�['']*len(diff_flags)
|
|
|
|
��������num_chg,�in_change�=�0,�False
|
|
|
|
��������last�=�0
|
|
|
|
��������for�i,flag�in�enumerate(diff_flags):
|
|
|
|
������������if�flag:
|
|
|
|
����������������if�not�in_change:
|
|
|
|
��������������������in_change�=�True
|
|
|
|
��������������������last�=�i
|
|
|
|
��������������������#�at�the�beginning�of�a�change,�drop�an�anchor�a�few�lines
|
|
|
|
��������������������#�(the�context�lines)�before�the�change�for�the�previous�
|
|
|
|
��������������������#�link
|
|
|
|
��������������������i�=�max([0,i-numlines])
|
|
|
|
��������������������next_id[i]�=�'�id="difflib_chg_%s_%d"'�%�(prefix,num_chg)
|
|
|
|
��������������������#�at�the�beginning�of�a�change,�drop�a�link�to�the�next�
|
|
|
|
��������������������#�change
|
|
|
|
��������������������num_chg�+=�1
|
|
|
|
��������������������next_href[last]�=�'<a�href="#difflib_chg_%s_%d">n</a>'�%�(
|
|
|
|
�������������������������prefix,num_chg)
|
|
|
|
������������else:
|
|
|
|
����������������in_change�=�False
|
|
|
|
��������#�if�not�a�change�on�first�line,�drop�a�link
|
|
|
|
��������if�not�diff_flags[0]:
|
|
|
|
������������next_href[0]�=�'<a�href="#difflib_chg_%s_0">f</a>'�%�prefix
|
|
|
|
��������#�redo�the�last�link�to�link�to�the�top
|
|
|
|
��������next_href[last]�=�'<a�href="#difflib_chg_%s_top">t</a>'�%�(prefix)
|
|
|
|
��������import�cStringIO
|
|
|
|
��������s�=�cStringIO.StringIO()
|
|
|
|
��������for�i�in�range(len(diff_flags)):
|
|
|
|
������������if�diff_flags[i]�is�None:
|
|
|
|
����������������#�mdiff�yields�None�on�separator�lines
|
|
|
|
����������������s.write('</tbody><tbody>\n')
|
|
|
|
������������else:
|
|
|
|
����������������s.write('<tr>%s\n<td�class="diff_next"%s>%s</td>\n%s\n</tr>'�%
|
|
|
|
������������������������(from_text[i],next_id[i],next_href[i],to_text[i]))
|
|
|
|
��������if�fromdesc�or�todesc:
|
|
|
|
������������header_row�=�'<thead><tr>%s%s%s</tr></thead>'�%�(
|
|
|
|
����������������'<th�colspan="2"�class="diff_header">%s</th>'�%�fromdesc,
|
|
|
|
����������������'<th�class="diff_next"><br�/></th>',
|
|
|
|
����������������'<th�colspan="2"�class="diff_header">%s</th>'�%�todesc)
|
|
|
|
��������else:
|
|
|
|
������������header_row�=�''
|
|
|
|
��������return�self.table_template�%�dict(
|
|
|
|
������������summary=summary,
|
|
|
|
������������data_rows=s.getvalue(),
|
|
|
|
������������header_row=header_row,
|
|
|
|
������������prefix=prefix)
|
|
|
|
�
|
|
|
|
����def�format_line(self,side,linenum,text):
|
|
|
|
��������"""Returns�marked�up�"from"�or�"to"�text�line
|
|
|
|
�
|
|
|
|
��������mdiff()�will�call�this�function�with�the�following�arguments:
|
|
|
|
�
|
|
|
|
��������side�--�0�or�1�indicating�"from"�or�"to"�text
|
|
|
|
��������linenum�--�line�number�(used�for�line�number�column)
|
|
|
|
��������text�--�line�text�to�be�marked�up
|
|
|
|
��������"""
|
|
|
|
��������try:
|
|
|
|
������������linenum�=�self.linenum_template�%�linenum
|
|
|
|
������������id�=�'�id="%s%s"'�%�(self._prefix[side],linenum)
|
|
|
|
��������except�TypeError:
|
|
|
|
������������#�handle�blank�lines�where�linenum�is�None
|
|
|
|
������������linenum�=�''
|
|
|
|
������������id�=�''�
|
|
|
|
��������text�=�self._escape(text)
|
|
|
|
��������text�=�text.replace('�',' ')
|
|
|
|
��������fmt�=�'<td�class="diff_header"%s>%s</td><td�nowrap="nowrap">%s</td>'
|
|
|
|
��������return�fmt�%�(id,linenum,text)
|
|
|
|
�
|
|
|
|
����def�format_change(self,side,linenum,text,type):
|
|
|
|
��������"""Returns�HTML�highlighted�text
|
|
|
|
�
|
|
|
|
��������mdiff()�will�call�this�function�with�the�following�arguments:
|
|
|
|
��������side�--�0�or�1�indicating�"from"�or�"to"�text
|
|
|
|
��������linenum�--�line�number�that�contains�the�text�(used�for�creating�
|
|
|
|
�������������������"next"�links)
|
|
|
|
��������text�--�text�to�be�highlighted
|
|
|
|
��������type�--�+/-/^�indicating�type�of�change
|
|
|
|
��������"""
|
|
|
|
��������if�type�==�'+':
|
|
|
|
������������return�'<span�class="diff_add">%s</span>'�%�text
|
|
|
|
��������elif�type�==�'-':
|
|
|
|
������������return�'<span�class="diff_sub">%s</span>'�%�text
|
|
|
|
��������#�must�be�'^':
|
|
|
|
��������return�'<span�class="diff_chg">%s</span>'�%�text
|
|
|
|
�
|
|
|
|
del�re
|
|
|
|
|
| def�restore(delta,�which):
|
|
def�restore(delta,�which):
|
| ����r"""
|
|
����r"""
|
| ����Generate�one�of�the�two�sequences�that�generated�a�delta.
|
|
����Generate�one�of�the�two�sequences�that�generated�a�delta.
|
|
|
|
|
| ����Given�a�`delta`�produced�by�`Differ.compare()`�or�`ndiff()`,�extract
|
|
����Given�a�`delta`�produced�by�`Differ.compare()`�or�`ndiff()`,�extract
|
| ����lines�originating�from�file�1�or�2�(parameter�`which`),�stripping�off�line
|
|
����lines�originating�from�file�1�or�2�(parameter�`which`),�stripping�off�line
|
| ����prefixes.
|
|
����prefixes.
|
|
|
|
|
| ����Examples:
|
|
����Examples:
|
|
|
|
|