rmorshea / spectate

Observe the evolution of mutable data types like lists, dicts, and sets.

Home Page:https://python-spectate.readthedocs.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Rework of Oracle

rmorshea opened this issue · comments

Proposed rework of the Oracle model type that was recently removed due to bugs:

import functools
from spectate import mvc


class MetaOracle(type):
    
    def __init__(self, name, bases, attrs):
        if "_model_capture_types" in attrs:
            class_to_key = functools.cmp_to_key(lambda t1, t2: -1 if issubclass(t1, t2) else 1)
            class_key_from_item = lambda i: class_to_key(i[1])
            self._model_capture_order = tuple(sorted(self._model_capture_types.items(), key=class_key_from_item))


class Oracle(mvc.Object, metaclass=MetaOracle):

    _model_capture_types = {
        "_capture_dict": mvc.Dict,
        "_capture_object": mvc.Object,
        "_capture_list": mvc.List,
    }

    def _capture_model_events(self, model, path=()):
        mvc.view(model)(lambda *args, **kwargs: self._capture(path=path, *args, **kwargs))

    def _capture(self, value, events, path):
        for method, model_type in self._model_capture_order:
            if isinstance(value, model_type):
                super()._notify_model_views(tuple(
                    getattr(self, method)(value, e, path)
                    for e in events
                ))
                break
        else:
            raise TypeError("Oracle has no capture type %r" % value)

    def _capture_object(self, value, event, path):
        path += (event.attr,)
        if isinstance(event.new, mvc.Model):
            self._capture_model_events(event.new, path)
        return event["path": path]

    def _capture_dict(self, value, event, path):
        path += (event.key,)
        if isinstance(event.new, mvc.Model):
            self._capture_model_events(event.new, path)
        return event["path": path]
    
    def _capture_list(self, value, event, path):
        path += (event.index,)
        if isinstance(event.new, mvc.Model):
            self._capture_model_events(event.new, path)
        return event["path": path]
    
    def _capture_set(self, value, event, path):
        # We don't need to capture the contents of a set because
        # anything stored in it must be hashable and thus not mutable
        return event["path": path]

    def _notify_model_views(self, events):
        self._capture(self, events, path=())

closed by #33