crossbario / autobahn-python

WebSocket and WAMP in Python for Twisted and asyncio
https://crossbar.io/autobahn
MIT License
2.48k stars 769 forks source link

Autobahn shared core API #472

Closed oberstet closed 6 years ago

oberstet commented 9 years ago

As we have been discussing various improvements to the AutobahnPython WAMP API like the new Connection class and just using a WAMP Session object instead of being forced to inherit from a Session base class, now is the chance to design a core API for WAMP that is highly similar between:

oberstet commented 7 years ago

and here is yet another try ... how do you like that one?

SUBSCRIBE:

transport = await client.connect(u'wss://example.com')

session = await transport.join(u'example1')

subscription = await session.subscribe(u'com.example.on_hello')

def on_hello(msg, seq, opt=None):
    print('Received {} - {} - {}'.format(msg, seq, opt))

subscription.on(on_hello)

await sleep(60)

await subscription.unsubscribe()

await session.leave()

await transport.disconnect()
oberstet commented 7 years ago
PUBLISH:

transport = await client.connect(u'wss://example.com')

session = await transport.join(u'example1')

seq = 0
while seq < 60:
    session.publish(u'com.example.on_hello', u'Hello, world!', seq, opt=[1, 2, 3])
    await sleep(1)

await session.leave()

await transport.disconnect()
meejah commented 7 years ago

I like the general flow -- and I think a "one-time connect" method is a good idea (vs. e.g. the more "run"/"start" style ones which do re-connection too). (That is, both should be possible)

I'm not sure about the "sub = await subscribe(topic)" followed by the ".on()" thing -- what's the use-case? (i.e. when would you not do the .on() right away). What about allowing decorator-access? (ideally "in addition to" ...). Like so:

session = await ...

@session.subscribe(u'com.example.hello')
def on_hello(name):
    print("hello", name)
meejah commented 7 years ago

Further to the decorator idea, via the existing Component:

component = wamp.Component(...)

@component.on_join
async def connect_to_db(session, details):
    db = await txpostgres.Connection().connect(...)

(Similar for the other events you can subscribe to on Component of course).

oberstet commented 7 years ago

I think the discussion is heading towards consensus (inheritance = bad, observers = good, ..). One thing though is to define/document the events that can be observed on a session.

Eg., currently we have these https://github.com/crossbario/autobahn-python/blob/master/autobahn/wamp/component.py#L398 events:

  1. start
  2. connect: fired when a transport is connected to the session (the transport itself must be itself "connected" at the network level to be of use to the session of course)
  3. join: fired when the WAMP opening handshake is completed (HELLO -> CHALLENGE -> AUTHENTICATE -> WELCOME)
  4. ready: fired when ALL observers on "join" have finished
  5. leave
  6. disconnect: wired when the transport is disconnected from the session (the transport itself might stay "connected" at the network level to be reused with the same or a different session)

@meejah what was start for again?

also, we only have leave, not left (or similar) - does it make sense to distinguish between "right before WAMP closing handshake is started" and "ALL former "leave" user observers are done and the WAMP closing handshake is finished."

oberstet commented 7 years ago

So a transport is "connected" at the network level (TCP connection is established), and then "connected" to a session.

"connected" is hence overloaded .. but we could also go with:

Any preferences?

meejah commented 7 years ago

I think "start" is just fired when you "started running your Component" .. but yes is should probably have a matching "done" or "stopped" or similar if we want it?

I think "join" / "leave" and "connect" / "disconnect" are the right pairs of words? (or it would want to be "joined" and "left").

IIRC we had semantics that the future returned from Component.start() would fire when the thing was "done" (which is after all on-leave listeners are completed) so that points to having a "done" event mirroring "ready" (i.e. "ready" fires after join + all listeners, "done" fires after "leave" and all listeners)...but looking at it now maybe that would be more clear if the events were "join", "joined" and "leave", "left"? And then keep "connect" / "disconnect".

Looking in the code, "start" currently fires as soon as something calls .start on a Component .. so I guess a matching "end" event would probably make sense too (which fires at the same time as the future returned from Component.start).

Then the overall picture would be something like: start -> connected -> join -> joined/ready -> leave -> left -> disconnected -> end. The "connected -> ... -> disconnected" part would be a loop while doing retrys or re-connects and so "end" would only fire after we'd given up on all transports and retry/reconnect attempts (or were told stop).

meejah commented 7 years ago

I think saying "a Session is attached to a transport" (or I guess "a transport is attached to a session") makes the most sense and is least ambiguous.

oberstet commented 6 years ago

subseded/fixed in https://github.com/crossbario/autobahn-python/issues/964