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_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_serialize(self): hs = HistoryState("example", [self.resource]) self.assertEqual( hs.serialize(), {"hash": "example", "resources": hs.serialize_resources(), "handler": "/handler.lua"} )
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
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