Source code for pyams_zodbbrowser.diff

#
# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#

__docformat__ = 'restructuredtext'


# import standard library
from html import escape

# import interfaces
from pyams_zodbbrowser.interfaces import IValueRenderer

# import packages


ADDED = 'added'
REMOVED = 'removed'
CHANGED = 'changed to'


[docs]def compareDicts(new, old): """Compare two state dictionaries, return dict.""" diffs = {} for key, value in list(new.items()): if key not in old: diffs[key] = (ADDED, value, None) elif old[key] != value: diffs[key] = (CHANGED, value, old[key]) for key, value in list(old.items()): if key not in new: diffs[key] = (REMOVED, None, value) return diffs
[docs]def isascii(s): """See if the string can be safely converted to unicode.""" try: s.encode('ascii') except UnicodeError: return False else: return True
[docs]def compareTuples(new, old): """Compare two tuples. Return (common_prefix, middle_of_old, middle_of_new, common_suffix) """ first = 0 for oldval, newval in zip(old, new): if oldval != newval: break first += 1 last = 0 for oldval, newval in zip(reversed(old[first:]), reversed(new[first:])): if oldval != newval: break last += 1 return (old[:first], old[first:len(old) - last], new[first:len(new) - last], old[len(old) - last:])
[docs]def compareTuplesHTML(new, old, tid=None, indent=''): """Compare two tuples, return HTML.""" html = [indent + '<div class="diff">\n'] prefix, removed, added, suffix = compareTuples(new, old) if len(prefix) > 0: html.append(indent + ' <div class="diffitem %s">\n' % 'same') if len(prefix) == 1: html.append(indent + ' first item kept the same\n') else: html.append(indent + ' first %d items kept the same\n' % len(prefix)) html.append(indent + ' </div>\n') for oldval in removed: html.append(indent + ' <div class="diffitem %s">\n' % REMOVED) html.append(indent + ' %s %s\n' % ( REMOVED, IValueRenderer(oldval).render(tid))) html.append(indent + ' </div>\n') for newval in added: html.append(indent + ' <div class="diffitem %s">\n' % ADDED) html.append(indent + ' %s %s\n' % ( ADDED, IValueRenderer(newval).render(tid))) html.append(indent + ' </div>\n') if len(suffix) > 0: html.append(indent + ' <div class="diffitem %s">\n' % 'same') if len(suffix) == 1: html.append(indent + ' last item kept the same\n') else: html.append(indent + ' last %d items kept the same\n' % len(suffix)) html.append(indent + ' </div>\n') html.append(indent + '</div>\n') return ''.join(html)
[docs]def compareDictsHTML(new, old, tid=None, indent=''): """Compare two state dictionaries, return HTML.""" html = [indent + '<div class="diff">\n'] diff = compareDicts(new, old) for key, (action, newvalue, oldvalue) in sorted(list(diff.items()), key=lambda k_v: (str(type(k_v[0])), k_v[0])): what = action.split()[0] html.append(indent + ' <div class="diffitem %s">\n' % escape(what)) if isinstance(key, str) and isascii(key): html.append(indent + ' <strong>%s</strong>: ' % escape(key)) else: html.append(indent + ' <strong>%s</strong>: ' % IValueRenderer(key).render(tid)) if (action == CHANGED and isinstance(oldvalue, dict) and isinstance(newvalue, dict)): html.append('dictionary changed:\n') html.append(compareDictsHTML(newvalue, oldvalue, tid, indent=indent + ' ')) elif (action == CHANGED and isinstance(oldvalue, tuple) and isinstance(newvalue, tuple)): html.append('tuple changed:\n') html.append(compareTuplesHTML(newvalue, oldvalue, tid, indent=indent + ' ')) else: html.append(action) html.append(' ') if action == REMOVED: value = oldvalue else: value = newvalue html.append(IValueRenderer(value).render(tid)) html.append('\n') html.append(indent + ' </div>\n') html.append(indent + '</div>\n') return ''.join(html)