import io import difflib def sumLen(lines): return sum(map(len, lines)) def diff(old, new, limit=False): matcher = difflib.SequenceMatcher(None, old, new) actions = [] size = 0 for tag, old_from, old_to, new_from, new_to in matcher.get_opcodes(): if tag == "insert": new_line = new[new_from:new_to] actions.append(("+", new_line)) size += sum(map(len, new_line)) elif tag == "equal": actions.append(("=", sumLen(old[old_from:old_to]))) elif tag == "delete": actions.append(("-", sumLen(old[old_from:old_to]))) elif tag == "replace": actions.append(("-", sumLen(old[old_from:old_to]))) new_lines = new[new_from:new_to] actions.append(("+", new_lines)) size += sumLen(new_lines) if limit and size > limit: return False return actions def patch(old_f, actions): new_f = io.BytesIO() for action, param in actions: if type(action) is bytes: action = action.decode() if action == "=": # Same lines new_f.write(old_f.read(param)) elif action == "-": # Delete lines old_f.seek(param, 1) # Seek from current position continue elif action == "+": # Add lines for add_line in param: new_f.write(add_line) else: raise "Unknown action: %s" % action return new_f