Further refinements
This commit is contained in:
parent
21aa08e75b
commit
026d1ec151
67
chat.py
67
chat.py
|
@ -2,8 +2,8 @@ import sys
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
import syndicate
|
import syndicate
|
||||||
from syndicate import patterns as P, actor, dataspace
|
from syndicate import patterns as P, actor, dataspace, gatekeeper
|
||||||
from syndicate.schema import simpleChatProtocol, gatekeeper, sturdy
|
from syndicate.schema import simpleChatProtocol, sturdy
|
||||||
from syndicate.during import During
|
from syndicate.during import During
|
||||||
|
|
||||||
Present = simpleChatProtocol.Present
|
Present = simpleChatProtocol.Present
|
||||||
|
@ -13,51 +13,30 @@ conn_str = '<ws "ws://localhost:8001/">'
|
||||||
cap_str = '<ref "syndicate" [] #[pkgN9TBmEd3Q04grVG4Zdw==]>'
|
cap_str = '<ref "syndicate" [] #[pkgN9TBmEd3Q04grVG4Zdw==]>'
|
||||||
cap = sturdy.SturdyRef.decode(syndicate.parse(cap_str))
|
cap = sturdy.SturdyRef.decode(syndicate.parse(cap_str))
|
||||||
|
|
||||||
# sys.stderr.write(
|
|
||||||
# 'Usage: chat.py [ <tcp "HOST" PORT> | <ws "ws://HOST[:PORT]/"> | <unix "PATH"> ]\n')
|
|
||||||
# sys.exit(1)
|
|
||||||
|
|
||||||
me = 'user_' + str(random.randint(10, 1000))
|
|
||||||
|
|
||||||
_print = print
|
|
||||||
def print(*items):
|
|
||||||
_print(*items)
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
def main_facet(turn, root_facet, ds):
|
|
||||||
print('main_facet', ds)
|
|
||||||
f = turn._facet
|
|
||||||
turn.publish(ds, Present(me))
|
|
||||||
|
|
||||||
@dataspace.during(turn, ds, P.rec('Present', P.CAPTURE))
|
|
||||||
def on_presence(turn, who):
|
|
||||||
print('%s joined' % (who,))
|
|
||||||
return lambda turn: print('%s left' % (who,))
|
|
||||||
|
|
||||||
@dataspace.on_message(turn, ds, P.rec('Says', P.CAPTURE, P.CAPTURE))
|
|
||||||
def on_says(turn, who, what):
|
|
||||||
print('%s says %r' % (who, what))
|
|
||||||
|
|
||||||
@turn.linked_task()
|
|
||||||
async def accept_input():
|
|
||||||
reader = asyncio.StreamReader()
|
|
||||||
await actor.find_loop().connect_read_pipe(lambda: asyncio.StreamReaderProtocol(reader), sys.stdin)
|
|
||||||
while line := (await reader.readline()).decode('utf-8'):
|
|
||||||
actor.Turn.external(f, lambda turn: turn.send(ds, Says(me, line.strip())))
|
|
||||||
actor.Turn.external(f, lambda turn: turn.stop(root_facet))
|
|
||||||
|
|
||||||
def main(turn):
|
def main(turn):
|
||||||
root_facet = turn._facet
|
root_facet = turn._facet
|
||||||
|
|
||||||
@During().add_handler
|
@syndicate.relay.connect(turn, conn_str, cap)
|
||||||
def handle_gatekeeper(turn, gk):
|
def on_connected(turn, ds):
|
||||||
@During().add_handler
|
me = 'user_' + str(random.randint(10, 1000))
|
||||||
def handle_ds(turn, ds):
|
|
||||||
return turn.facet(lambda turn: main_facet(turn, root_facet, ds.embeddedValue))
|
|
||||||
turn.publish(gk.embeddedValue, gatekeeper.Resolve(cap, turn.ref(handle_ds)))
|
|
||||||
|
|
||||||
conn = syndicate.relay.TunnelRelay.from_str(turn,
|
turn.publish(ds, Present(me))
|
||||||
conn_str,
|
|
||||||
gatekeeper_peer = turn.ref(handle_gatekeeper))
|
@dataspace.during(turn, ds, P.rec('Present', P.CAPTURE), inert_ok=True)
|
||||||
|
def on_presence(turn, who):
|
||||||
|
print('%s joined' % (who,))
|
||||||
|
turn.on_stop(lambda turn: print('%s left' % (who,)))
|
||||||
|
|
||||||
|
@dataspace.on_message(turn, ds, P.rec('Says', P.CAPTURE, P.CAPTURE))
|
||||||
|
def on_says(turn, who, what):
|
||||||
|
print('%s says %r' % (who, what))
|
||||||
|
|
||||||
|
@turn.linked_task()
|
||||||
|
async def accept_input(f):
|
||||||
|
reader = asyncio.StreamReader()
|
||||||
|
await actor.find_loop().connect_read_pipe(lambda: asyncio.StreamReaderProtocol(reader), sys.stdin)
|
||||||
|
while line := (await reader.readline()).decode('utf-8'):
|
||||||
|
actor.Turn.external(f, lambda turn: turn.send(ds, Says(me, line.strip())))
|
||||||
|
actor.Turn.external(f, lambda turn: turn.stop(root_facet))
|
||||||
|
|
||||||
actor.start_actor_system(main, name = 'chat', debug = False)
|
actor.start_actor_system(main, name = 'chat', debug = False)
|
||||||
|
|
|
@ -101,14 +101,14 @@ class Actor:
|
||||||
queue_task(finish_termination)
|
queue_task(finish_termination)
|
||||||
|
|
||||||
class Facet:
|
class Facet:
|
||||||
def __init__(self, actor, parent, initial_assertions = {}):
|
def __init__(self, actor, parent, initial_assertions = None):
|
||||||
self.id = next(_next_facet_id)
|
self.id = next(_next_facet_id)
|
||||||
self.actor = actor
|
self.actor = actor
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
if parent:
|
if parent:
|
||||||
parent.children.add(self)
|
parent.children.add(self)
|
||||||
self.children = set()
|
self.children = set()
|
||||||
self.outbound = initial_assertions
|
self.outbound = initial_assertions or {}
|
||||||
self.shutdown_actions = []
|
self.shutdown_actions = []
|
||||||
self.linked_tasks = []
|
self.linked_tasks = []
|
||||||
self.alive = True
|
self.alive = True
|
||||||
|
@ -154,7 +154,7 @@ class Facet:
|
||||||
self.inert_check_preventers = self.inert_check_preventers - 1
|
self.inert_check_preventers = self.inert_check_preventers - 1
|
||||||
return disarm
|
return disarm
|
||||||
|
|
||||||
def linked_task(self, coro, loop = None):
|
def linked_task(self, coro_fn, loop = None):
|
||||||
task = None
|
task = None
|
||||||
def cancel_linked_task(turn):
|
def cancel_linked_task(turn):
|
||||||
nonlocal task
|
nonlocal task
|
||||||
|
@ -166,7 +166,7 @@ class Facet:
|
||||||
self.actor.cancel_at_exit(cancel_linked_task)
|
self.actor.cancel_at_exit(cancel_linked_task)
|
||||||
async def guarded_task():
|
async def guarded_task():
|
||||||
try:
|
try:
|
||||||
await coro
|
await coro_fn(self)
|
||||||
finally:
|
finally:
|
||||||
Turn.external(self, cancel_linked_task)
|
Turn.external(self, cancel_linked_task)
|
||||||
task = find_loop(loop).create_task(guarded_task())
|
task = find_loop(loop).create_task(guarded_task())
|
||||||
|
@ -267,6 +267,7 @@ class Turn:
|
||||||
def ref(self, entity):
|
def ref(self, entity):
|
||||||
return Ref(self._facet, entity)
|
return Ref(self._facet, entity)
|
||||||
|
|
||||||
|
# this actually can work as a decorator as well as a normal method!
|
||||||
def facet(self, boot_proc):
|
def facet(self, boot_proc):
|
||||||
new_facet = Facet(self._facet.actor, self._facet)
|
new_facet = Facet(self._facet.actor, self._facet)
|
||||||
with ActiveFacet(self, new_facet):
|
with ActiveFacet(self, new_facet):
|
||||||
|
@ -278,7 +279,7 @@ class Turn:
|
||||||
|
|
||||||
# decorator
|
# decorator
|
||||||
def linked_task(self, loop = None):
|
def linked_task(self, loop = None):
|
||||||
return lambda thunk: self._facet.linked_task(thunk(), loop = loop)
|
return lambda coro_fn: self._facet.linked_task(coro_fn, loop = loop)
|
||||||
|
|
||||||
def stop(self, facet = None, continuation = None):
|
def stop(self, facet = None, continuation = None):
|
||||||
if facet is None:
|
if facet is None:
|
||||||
|
@ -289,6 +290,10 @@ class Turn:
|
||||||
continuation(turn)
|
continuation(turn)
|
||||||
self._enqueue(facet.parent, action)
|
self._enqueue(facet.parent, action)
|
||||||
|
|
||||||
|
# can also be used as a decorator
|
||||||
|
def on_stop(self, a):
|
||||||
|
self._facet.on_stop(a)
|
||||||
|
|
||||||
def spawn(self, boot_proc, name = None, initial_assertions = None, daemon = False):
|
def spawn(self, boot_proc, name = None, initial_assertions = None, daemon = False):
|
||||||
def action(turn):
|
def action(turn):
|
||||||
new_outbound = {}
|
new_outbound = {}
|
||||||
|
@ -404,6 +409,10 @@ class OutboundAssertion:
|
||||||
self.ref = ref
|
self.ref = ref
|
||||||
self.established = False
|
self.established = False
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<OutboundAssertion handle=%s ref=%r%s>' % \
|
||||||
|
(self.handle, self.ref, ' established' if self.established else '')
|
||||||
|
|
||||||
# Can act as a mixin
|
# Can act as a mixin
|
||||||
class Entity:
|
class Entity:
|
||||||
def on_publish(self, turn, v, handle):
|
def on_publish(self, turn, v, handle):
|
||||||
|
|
|
@ -9,9 +9,9 @@ def observe(turn, ds, pattern):
|
||||||
return publish_observer
|
return publish_observer
|
||||||
|
|
||||||
# decorator
|
# decorator
|
||||||
def on_message(turn, ds, pattern):
|
def on_message(turn, ds, pattern, *args, **kwargs):
|
||||||
return lambda on_msg: observe(turn, ds, pattern)(During().msg_handler(on_msg))
|
return lambda on_msg: observe(turn, ds, pattern)(During(*args, **kwargs).msg_handler(on_msg))
|
||||||
|
|
||||||
# decorator
|
# decorator
|
||||||
def during(turn, ds, pattern):
|
def during(turn, ds, pattern, *args, **kwargs):
|
||||||
return lambda on_add: observe(turn, ds, pattern)(During().add_handler(on_add))
|
return lambda on_add: observe(turn, ds, pattern)(During(*args, **kwargs).add_handler(on_add))
|
||||||
|
|
|
@ -7,12 +7,13 @@ def _default_sync(turn, peer):
|
||||||
turn.send(peer, True)
|
turn.send(peer, True)
|
||||||
|
|
||||||
class During(actor.Entity):
|
class During(actor.Entity):
|
||||||
def __init__(self, on_add=None, on_msg=None, on_sync=None, name=None):
|
def __init__(self, on_add=None, on_msg=None, on_sync=None, name=None, inert_ok=False):
|
||||||
self.retract_handlers = {}
|
self.facets = {}
|
||||||
self._on_add = on_add or _ignore
|
self._on_add = on_add or _ignore
|
||||||
self._on_msg = on_msg or _ignore
|
self._on_msg = on_msg or _ignore
|
||||||
self._on_sync = on_sync or _default_sync
|
self._on_sync = on_sync or _default_sync
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.inert_ok = inert_ok
|
||||||
self.flatten_arg = True
|
self.flatten_arg = True
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -24,20 +25,15 @@ class During(actor.Entity):
|
||||||
return v if self.flatten_arg and isinstance(v, tuple) else (v,)
|
return v if self.flatten_arg and isinstance(v, tuple) else (v,)
|
||||||
|
|
||||||
def on_publish(self, turn, v, handle):
|
def on_publish(self, turn, v, handle):
|
||||||
retract_handler = self._on_add(turn, *self._wrap(v))
|
facet = turn.facet(lambda turn: self._on_add(turn, *self._wrap(v)))
|
||||||
if retract_handler is not None:
|
if self.inert_ok:
|
||||||
if isinstance(retract_handler, actor.Facet):
|
facet.prevent_inert_check()
|
||||||
self.retract_handlers[handle] = lambda turn: turn.stop(retract_handler)
|
self.facets[handle] = facet
|
||||||
elif callable(retract_handler):
|
|
||||||
self.retract_handlers[handle] = retract_handler
|
|
||||||
else:
|
|
||||||
raise ValueError('Non-callable retract_handler', {
|
|
||||||
'retract_handler': retract_handler,
|
|
||||||
'on_add': self._on_add,
|
|
||||||
})
|
|
||||||
|
|
||||||
def on_retract(self, turn, handle):
|
def on_retract(self, turn, handle):
|
||||||
self.retract_handlers.pop(handle, lambda turn: ())(turn)
|
facet = self.facets.pop(handle, None)
|
||||||
|
if facet is not None:
|
||||||
|
turn.stop(facet)
|
||||||
|
|
||||||
def on_message(self, turn, v):
|
def on_message(self, turn, v):
|
||||||
self._on_msg(turn, *self._wrap(v))
|
self._on_msg(turn, *self._wrap(v))
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
from .schema import gatekeeper
|
||||||
|
from .during import During
|
||||||
|
|
||||||
|
# decorator
|
||||||
|
def resolve(turn, gk, cap, *args, **kwargs):
|
||||||
|
def configure_handler(handler):
|
||||||
|
def unwrapping_handler(turn, wrapped_ref):
|
||||||
|
return handler(turn, wrapped_ref.embeddedValue)
|
||||||
|
return _resolve(turn, gk, cap)(During(*args, **kwargs).add_handler(unwrapping_handler))
|
||||||
|
return configure_handler
|
||||||
|
|
||||||
|
# decorator
|
||||||
|
def _resolve(turn, gk, cap):
|
||||||
|
def publish_resolution_request(entity):
|
||||||
|
turn.publish(gk, gatekeeper.Resolve(cap, turn.ref(entity)))
|
||||||
|
return entity
|
||||||
|
return publish_resolution_request
|
|
@ -5,7 +5,8 @@ import logging
|
||||||
from preserves import Embedded, stringify
|
from preserves import Embedded, stringify
|
||||||
from preserves.fold import map_embeddeds
|
from preserves.fold import map_embeddeds
|
||||||
|
|
||||||
from . import actor, encode, transport, Decoder
|
from . import actor, encode, transport, Decoder, gatekeeper
|
||||||
|
from .during import During
|
||||||
from .actor import _inert_ref, Turn
|
from .actor import _inert_ref, Turn
|
||||||
from .idgen import IdGenerator
|
from .idgen import IdGenerator
|
||||||
from .schema import externalProtocol as protocol, sturdy, transportAddress
|
from .schema import externalProtocol as protocol, sturdy, transportAddress
|
||||||
|
@ -70,9 +71,10 @@ class TunnelRelay:
|
||||||
self.gatekeeper_peer = gatekeeper_peer
|
self.gatekeeper_peer = gatekeeper_peer
|
||||||
self.gatekeeper_oid = gatekeeper_oid
|
self.gatekeeper_oid = gatekeeper_oid
|
||||||
self._reset()
|
self._reset()
|
||||||
self.facet.linked_task(self._reconnecting_main(asyncio.get_running_loop(),
|
self.facet.linked_task(
|
||||||
on_connected = on_connected,
|
lambda facet: self._reconnecting_main(asyncio.get_running_loop(),
|
||||||
on_disconnected = on_disconnected))
|
on_connected = on_connected,
|
||||||
|
on_disconnected = on_disconnected))
|
||||||
|
|
||||||
def _reset(self):
|
def _reset(self):
|
||||||
self.inbound_assertions = {} # map remote handle to InboundAssertion
|
self.inbound_assertions = {} # map remote handle to InboundAssertion
|
||||||
|
@ -237,8 +239,21 @@ class TunnelRelay:
|
||||||
should_run = await (on_disconnected or _default_on_disconnected)(self, did_connect)
|
should_run = await (on_disconnected or _default_on_disconnected)(self, did_connect)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_str(turn, s, **kwargs):
|
def from_str(turn, conn_str, **kwargs):
|
||||||
return transport.connection_from_str(turn, s, **kwargs)
|
return transport.connection_from_str(turn, conn_str, **kwargs)
|
||||||
|
|
||||||
|
# decorator
|
||||||
|
def connect(turn, conn_str, cap, **kwargs):
|
||||||
|
def prepare_resolution_handler(handler):
|
||||||
|
@During().add_handler
|
||||||
|
def handle_gatekeeper(turn, gk):
|
||||||
|
gatekeeper.resolve(turn, gk.embeddedValue, cap)(handler)
|
||||||
|
return transport.connection_from_str(
|
||||||
|
turn,
|
||||||
|
conn_str,
|
||||||
|
gatekeeper_peer = turn.ref(handle_gatekeeper),
|
||||||
|
**kwargs)
|
||||||
|
return prepare_resolution_handler
|
||||||
|
|
||||||
class RelayEntity(actor.Entity):
|
class RelayEntity(actor.Entity):
|
||||||
def __init__(self, relay, oid):
|
def __init__(self, relay, oid):
|
||||||
|
|
Loading…
Reference in New Issue