def test_clone(self):
        hs1 = HistoryState("example", [self.resource], "/stanley_gibbons.lua")
        hs2 = hs1.clone()

        # Each resource compared by ref, not value. Use serialization for that.
        self.assertNotEqual(hs1.resources, hs2.resources)
        self.assertEqual(hs1.serialize(), hs2.serialize())
    def test_create_interpreter(self):
        res = handler_resource("tag_team")
        hs = HistoryState("example", [res])
        interp = hs.create_interpreter()
        self.assertEqual(interp.can_read(identity("mitzi")), True)
        self.assertEqual(interp.can_write(identity("victor")), False)

        # Should not be the same/cached
        self.assertNotEqual(interp, hs.create_interpreter())
 def test_apply(self):
     res = handler_resource("tag_team")
     hs = HistoryState("example", [res])
     ev = Event(
         {"path": "/handler.lua", "property": "comment", "value": "An arbitrary comment"}, identity("mitzi"), None
     )
     hs.apply(ev)
     self.assertEqual(res.comment, "An arbitrary comment")
     self.assertEqual(hs.hash, ev.hash())
    def test_interpreter(self):
        res = handler_resource("tag_team")
        hs = HistoryState("example", [res])
        interp = hs.interpreter
        self.assertEqual(interp.can_read(identity("mitzi")), True)
        self.assertEqual(interp.can_write(identity("victor")), False)

        # Should be the same/cached
        self.assertEqual(interp, hs.interpreter)

        # Should not be the same/cached, after hash change
        hs.hash = "some other hash"
        self.assertNotEqual(interp, hs.interpreter)
예제 #5
0
    def deserialize(self, serial):
        self._current = HistoryState(doc=self)
        self._current.deserialize(serial['original'])
        self.freeze()

        for event in serial['events']:
            ev = Event(event['content'], event['author'], event['version'])
            self.external_event(ev)
예제 #6
0
 def __init__(self, name, resources=[], owner = None):
     self._name = name
     self._owner = None
     if owner:
         owner.own_document(self)
     self._initial = HistoryState(doc = self)
     self._current = HistoryState("current", resources, self)
     self._history = History([self._initial, self._current])
     self._qs = quorumspace.QuorumSpace(self)
     self.signals = {
         'enact-event': dispatch.Signal(),
         'recv-events': dispatch.Signal(
             providing_args=['qid','events']),
         'recv-state':dispatch.Signal(
             providing_args=['qid','state']),
     }
     for res in resources:
         self.add_resource(res, False)
예제 #7
0
 def __init__(self, name, handler_path="/handler.lua", resources=[], owner = None):
     self._name = name
     self._owner = owner
     self._initial = HistoryState()
     self._current = HistoryState("current", resources, handler_path)
     self._history = History([self._initial, self._current])
     self._qs = quorumspace.QuorumSpace(self)
     self.signals = {
         'recv-version': dispatch.Signal(
             providing_args=['version']),
         'recv-block': dispatch.Signal(
             providing_args=['version','block']),
         'recv-snapshot':dispatch.Signal(
             providing_args=['version','snapshot']),
     }
     self.subscribers = set()
     for res in resources:
         self.add_resource(res, False)
    def test_deserialize(self):
        hs1 = HistoryState("example", [self.resource], "/stanley_gibbons.lua")
        hs2 = HistoryState()

        hs2.deserialize(hs1.serialize())

        # Test for value equality
        self.assertNotEqual(hs1.resources, hs2.resources)
        self.assertEqual(hs1.serialize(), hs2.serialize())
 def test_serialize(self):
     hs = HistoryState("example", [self.resource])
     self.assertEqual(
         hs.serialize(), {"hash": "example", "resources": hs.serialize_resources(), "handler": "/handler.lua"}
     )
 def test_serialize_resources(self):
     hs = HistoryState("example", [self.resource])
     self.assertEqual(
         hs.serialize_resources(),
         {"/": {"comment": "", "content": "", "path": "/", "type": "application/x-octet-stream"}},
     )
 def test_add_resource(self):
     hs = HistoryState()
     hs.add_resource(self.resource)
     self.assertEqual(hs.resources, {"/": self.resource})
예제 #12
0
class Document(object):
    def __init__(self, name, resources=[], owner = None):
        self._name = name
        self._owner = None
        if owner:
            owner.own_document(self)
        self._initial = HistoryState(doc = self)
        self._current = HistoryState("current", resources, self)
        self._history = History([self._initial, self._current])
        self._qs = quorumspace.QuorumSpace(self)
        self.signals = {
            'enact-event': dispatch.Signal(),
            'recv-events': dispatch.Signal(
                providing_args=['qid','events']),
            'recv-state':dispatch.Signal(
                providing_args=['qid','state']),
        }
        for res in resources:
            self.add_resource(res, False)

    # High-level resource manipulation

    def add_resource(self, resource, interp_call = True):
        if interp_call:
            self.interpreter.on_resource_update(resource.path, 'add')
        self._current.add_resource(resource)
        resource.document = self

    def get_resource(self, path):
        return self.resources[path]

    def del_resource(self, path, interp_call = True):
        if interp_call:
            self.interpreter.on_resource_update(path, 'delete')
        del self.resources[path]

    @property
    def resources(self):
        return self._current.resources

    @property
    def interpreter(self):
        return self._current.interpreter

    @property
    def subscribers(self):
        if self.owner:
            return self.owner.subscribers(self)
        else:
            return tuple()

    def serialize(self):
        return {
            'original': self._initial.serialize(),
            'events': self._history.events
        }

    def deserialize(self, serial):
        self._current = HistoryState(doc=self)
        self._current.deserialize(serial['original'])
        self.freeze()

        for event in serial['events']:
            ev = Event(event['content'], event['author'], event['version'])
            self.external_event(ev)

    def freeze(self):
        '''
        Throw away history and base originals off of current state.
        '''
        self._initial = self._current.clone()
        self._history.events = []

    def debug(self, lines):
        for line in lines:
            print(line)

    # Host requests

    def request(self, callback, *args):
        return self.interpreter.host_request(callback, args)

    # Event stuff

    def event(self, ev):
        '''
        Create a event from arbitrary object 'ev'
        '''
        if not self.can_write():
            raise ValueError("You don't have write permission")
        event = Event(ev, self.identity, self.version)
        quorum = Quorum(event, self._qs)
        return self.external_event(event)

    def external_event(self, event):
        if event.test(self._current):
            if self.owner:
                quorum = self.get_quorum(event)
                quorum.sign(self.identity)
                self.protocol.paxos.propose(self, event)
            else:
                event.enact(self)
            return event
        else:
            raise ValueError("Event %r was not valid" % event.content)

    def get_version(self, callback):
        if not self.can_read():
            raise ValueError("You don't have read permission")
        request = ReadRequest(self.identity)
        quorum = Quorum(request, self._qs)
        if self.owner:
            self.protocol._register(request.unique, callback)
            self.protocol.paxos.propose(self, request)
        return request
        
    def subscribe(self, callback, sources):
        if not self.can_read():
            raise ValueError("You don't have read permission")
        self.protocol.subscribe(self, callback, sources)

    def get_quorum(self, action):
        return self._qs.get_quorum(action)

    @property
    def competing(self):
        "All competing quorums"
        return self._qs.get_competing_actions()

    # Handler-derived properties

    def get_participants(self):
        return self.interpreter.quorum_participants()

    def get_thresholds(self):
        return self.interpreter.quorum_thresholds()

    def get_request_protocols(self):
        return self.interpreter.request_protocols()

    def can_read(self, ident = None):
        ident = ident or self.identity
        return self.interpreter.can_read(ident)

    def can_write(self, ident = None):
        ident = ident or self.identity
        return self.interpreter.can_write(ident)

    # Handler

    @property
    def handler(self):
        return self.get_resource('/handler')

    # Other accessors

    @property
    def name(self):
        return self._name

    @property
    def owner(self):
        return self._owner

    @property
    def protocol(self):
        return self.owner.protocol

    @property
    def identity(self):
        if self.owner:
            return self.owner.identity
        else:
            raise AttributeError("Attempt to access identity of unowned doc")

    @property
    def version(self):
        return self._current.hash
예제 #13
0
class Document(object):
    def __init__(self, name, handler_path="/handler.lua", resources=[], owner = None):
        self._name = name
        self._owner = owner
        self._initial = HistoryState()
        self._current = HistoryState("current", resources, handler_path)
        self._history = History([self._initial, self._current])
        self._qs = quorumspace.QuorumSpace(self)
        self.signals = {
            'recv-version': dispatch.Signal(
                providing_args=['version']),
            'recv-block': dispatch.Signal(
                providing_args=['version','block']),
            'recv-snapshot':dispatch.Signal(
                providing_args=['version','snapshot']),
        }
        self.subscribers = set()
        for res in resources:
            self.add_resource(res, False)

    # High-level resource manipulation

    def add_resource(self, resource, interp_call = True):
        if interp_call:
            self.interpreter.on_resource_update(resource.path, 'add')
        self._current.add_resource(resource)
        resource.document = self

    def get_resource(self, path):
        return self.resources[path]

    def del_resource(self, path, interp_call = True):
        if interp_call:
            self.interpreter.on_resource_update(path, 'delete')
        del self.resources[path]

    @property
    def resources(self):
        return self._current.resources

    @property
    def interpreter(self):
        return self._current.interpreter

    def snapshot(self, version = None):
        if version == None:
            version = self.version
        if version != self.version:
            raise NotImplementedError(
                "Rewinds not supported yet (have %r, %r requested)"
                % (self.version, version)
            )

        return self._current.serialize_resources()

    def serialize(self):
        return {
            'original': self._initial.serialize(),
            'events': self._history.events
        }

    def deserialize(self, serial):
        self._current = HistoryState()
        self._current.deserialize(serial['original'])
        self.freeze()

        for event in serial['events']:
            ev = Event(event['content'], event['author'], event['version'])
            self.external_event(ev)

    def freeze(self):
        '''
        Throw away history and base originals off of current state.
        '''
        self._initial = self._current.clone()
        self._history.events = []

    def eval(self, value):
        '''
        Evaluate code in handler context, returning result
        '''
        return self.interpreter.eval(value)

    def execute(self, value):
        '''
        Execute code in handler context
        '''
        return self.interpreter.execute(value)

    # Host requests

    def request(self, callback, *args):
        return self.interpreter.host_request(callback, args)

    # Event stuff

    def event(self, ev):
        '''
        Create a event from arbitrary object 'ev'
        '''
        if not self.can_write():
            raise ValueError("You don't have write permission")
        event = Event(ev, self.identity, self.version)
        quorum = Quorum(event, self._qs)
        return self.external_event(event)

    def external_event(self, event):
        if event.test(self._current):
            if self.owner:
                quorum = self.get_quorum(event)
                quorum.sign(self.identity)
                quorum.transmit_action(self)
            else:
                event.enact(self)
            return event
        else:
            raise ValueError("Event %r was not valid" % event.content)
        
    def subscribe(self):
        if not self.can_read():
            raise ValueError("You don't have read permission")
        request = ReadRequest(self.identity)
        quorum = Quorum(request, self._qs)
        if self.owner:
            quorum.transmit_action(self)
        return request

    def get_quorum(self, action):
        return self._qs.get_quorum(action)

    @property
    def competing(self):
        "All competing quorums"
        return self._qs.get_competing_actions()

    # Handler-derived properties

    def get_participants(self):
        return self.interpreter.quorum_participants()

    def get_thresholds(self):
        return self.interpreter.quorum_thresholds()

    def get_request_protocols(self):
        return self.interpreter.request_protocols()

    def can_read(self, ident = None):
        ident = ident or self.identity
        return self.interpreter.can_read(ident)

    def can_write(self, ident = None):
        ident = ident or self.identity
        return self.interpreter.can_write(ident)

    # Handler

    @property
    def handler(self):
        return self.get_resource(self._current.handler_path)

    def set_handler(self, path):
        self._current.handler_path = path

    # Other accessors

    @property
    def name(self):
        return self._name

    @property
    def owner(self):
        return self._owner

    @property
    def identity(self):
        if self.owner:
            return self.owner.identity
        else:
            raise AttributeError("Attempt to access identity of unowned doc")

    @property
    def version(self):
        return self._current.hash