Last active
March 26, 2016 22:33
-
-
Save eranimo/1e0e7669a1498099288d to your computer and use it in GitHub Desktop.
Generate changes of user classes by diffing the __dict__ property
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from copy import deepcopy as copy | |
from pprint import PrettyPrinter | |
pp = PrettyPrinter(indent=4) | |
echo = pp.pprint | |
class Change: | |
def __init__(self, type, key, value=None): | |
self.type = type | |
self.key = key | |
self.value = value | |
def __eq__(self, other): | |
return self.key == other.key and self.value == other.value and self.type == other.type | |
def __key__(self): | |
return self.type, self.key, self.value | |
def __hash__(self): | |
return hash(self.__key__()) | |
def __repr__(self): | |
return "<Change type='{}' key='{}' value='{}'".format(self.type, self.key, self.value) | |
def list_changes(old_list, new_list, key): | |
changes = [] | |
key_string = lambda index: key + '[' + str(index) + ']' | |
if len(old_list) == len(new_list): # list same length | |
for index, value in enumerate(new_list): | |
if value != old_list[index]: | |
changes.append(Change('set_index', key_string(index), value)) | |
elif len(old_list) < len(new_list): # list got bigger | |
for index, value in enumerate(new_list): | |
if index < len(old_list): | |
if value != old_list[index]: | |
changes.append(Change('set_index', key_string(index), value)) | |
else: | |
changes.append(Change('set_index', key_string(index), value)) | |
else: # list got smaller | |
for index, value in enumerate(old_list): | |
if index < len(new_list): | |
if value != new_list[index]: | |
changes.append(Change('set_index', key_string(index), value)) | |
else: | |
changes.append(Change('set_index', key_string(index), None)) | |
return changes | |
def dict_changes(old_dict, new_dict): | |
# print('compare', old_dict, new_dict) | |
changes = [] | |
for old_key in old_dict.keys(): | |
if old_key not in new_dict.keys(): | |
changes.append(Change('delete', old_key)) | |
for new_key, new_value in new_dict.items(): | |
if new_key not in old_dict.keys(): | |
changes.append(Change('set', new_key, new_value)) | |
else: | |
for old_key, old_value in old_dict.items(): | |
if new_key == old_key: | |
if new_value != old_value: | |
if isinstance(new_value, list): | |
changes.extend(list_changes(old_value, new_value, new_key)) | |
else: | |
changes.append(Change('update', new_key, new_value)) | |
return changes | |
class ChangeStore: | |
"A dictionary of models that reports the changes made to them within the last `tick`" | |
def __init__(self): | |
self.initial_tick = 0 | |
self.tick = self.initial_tick | |
self.store = {} | |
self.change_log = {} | |
self.change_log[0] = {} | |
self.store_updates = {} | |
self.store_updates[0] = {} | |
def model_get(self, model): | |
return copy(model.__dict__) | |
def add(self, model): | |
"Add a new model to the store" | |
self.store[model.id] = model | |
changes = dict_changes({}, self.model_get(model)) | |
self.change_log[self.tick][model.id] = changes | |
self.store_updates[self.tick][model.id] = self.model_get(model) | |
def changes(self): | |
"Reports what models have changed within the last tick" | |
# loop over every model, update if changed | |
if self.tick == self.initial_tick: | |
return self.change_log[self.initial_tick] | |
for id_num, model in self.store.items(): | |
self.store_updates[self.tick][id_num] = self.model_get(model) | |
# compile changes | |
for id_num, model in self.store.items(): | |
last_model = self.store_updates[self.tick-1][id_num] | |
current_model = self.model_get(model) | |
if current_model != last_model: | |
self.change_log[self.tick][id_num] = dict_changes(last_model, current_model) | |
return self.change_log[self.tick] | |
def next_tick(self): | |
self.tick += 1 | |
self.change_log[self.tick] = {} | |
self.store_updates[self.tick] = {} | |
return self.tick | |
if __name__ == "__main__": | |
class Foo: | |
def __init__(self, id_num, value): | |
self.id = id_num | |
self.value = value | |
self.s = set() | |
self.l = [] | |
def update_something(self, value): | |
self.value = value | |
def append_something(self, value): | |
self.l.append(value) | |
def __repr__(self): | |
return "<Foo id={} value={}>".format(self.id, self.value) | |
s = ChangeStore() | |
f = Foo('id-1', 'Foobar') | |
s.add(f) | |
echo(s.changes()) | |
s.next_tick() | |
f.update_something('asdasd') | |
f.append_something(123123) | |
f.s.add(1) | |
echo(s.changes()) | |
s.next_tick() | |
f.update_something('zzz') | |
f.append_something(123) | |
f.s.remove(1) | |
echo(s.changes()) | |
s.next_tick() | |
f.l = [0] | |
echo(s.changes()) | |
# output: | |
""" | |
{ 'id-1': [ <Change type='set' key='l' value='[]', | |
<Change type='set' key='value' value='Foobar', | |
<Change type='set' key='id' value='id-1', | |
<Change type='set' key='s' value='set()']} | |
{ 'id-1': [ <Change type='set_index' key='l[0]' value='123123', | |
<Change type='update' key='value' value='asdasd', | |
<Change type='update' key='s' value='{1}']} | |
{ 'id-1': [ <Change type='set_index' key='l[1]' value='123', | |
<Change type='update' key='value' value='zzz', | |
<Change type='update' key='s' value='set()']} | |
{ 'id-1': [ <Change type='set_index' key='l[0]' value='123123', | |
<Change type='set_index' key='l[1]' value='None']} | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment