Skip to content

Instantly share code, notes, and snippets.

@runekaagaard
Created November 2, 2019 12:53
Show Gist options
  • Save runekaagaard/d8572d68241d9be297f567de7b9f9852 to your computer and use it in GitHub Desktop.
Save runekaagaard/d8572d68241d9be297f567de7b9f9852 to your computer and use it in GitHub Desktop.
POC Python Change Monitor of generic nested data
from collections import namedtuple
ATTR_LOOKUP = 1
ITEM_LOOKUP = 2
Change = namedtuple("Change", "path to")
class Lookup(namedtuple("Lookup", "key lookup_type")):
def __str__(self):
if self.lookup_type == ATTR_LOOKUP:
return u".{}".format(self.key)
elif self.lookup_type == ITEM_LOOKUP:
return u"[{}]".format(repr(self.key))
else:
raise Exception("Unknown lookup type: {}".format(self.lookup_type))
def monitor_value(v, path, root):
if hasattr(v, "__getitem__") or hasattr(v, "__getattribute__"):
return ChangeMonitor(v, path, root)
else:
return v
class ChangeMonitor(object):
def __init__(self, data, path=None, root=None):
self._data_ = data
self._path_ = path or []
self._changes_ = []
self._root_ = root or self
def __getattribute__(self, k):
if k in ("_data_", "_path_", "_changes_", "_root_"):
return object.__getattribute__(self, k)
else:
path = self._path_[:]
path.append(Lookup(k, ATTR_LOOKUP))
return monitor_value(getattr(self._data_, k), path, self._root_)
def __getitem__(self, k):
path = self._path_[:]
path.append(Lookup(k, ITEM_LOOKUP))
return monitor_value(self._data_[k], path, self._root_)
def __setitem__(self, k, v):
self._root_._changes_.append(
Change(self._path_ + [Lookup(k, ITEM_LOOKUP)], v))
self._data_[k] = v
def __setattr__(self, k, v):
if k in ("_data_", "_path_", "_changes_", "_root_"):
return object.__setattr__(self, k, v)
self._root_._changes_.append(
Change(self._path_ + [Lookup(k, ATTR_LOOKUP)], v))
object.__setattr__(self, k, v)
def __repr__(self):
return repr(self._data_)
class Model(object):
name = "XXX"
m = Model()
# Wrap generic nested data in the change monitor.
x = ChangeMonitor({1: 2, 3: {4: 5, 6: {"7": {8: 9}}}, "m": m, "l": [1, 2, 3]})
x[3][4] = 42
x[3][6]["7"][8] = 9
x["m"].name = "YYY"
x["l"][1] = 999
print
print "The following is changed:"
for change in x._changes_:
print " DATA{} = {}".format(u"".join(unicode(x) for x in change.path),
repr(change.to))
The following is changed:
DATA[3][4] = 42
DATA[3][6]['7'][8] = 9
DATA['m'].name = 'YYY'
DATA['l'][1] = 999
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment