Package: autobahn-python-repl
A REPL environment for working with WAMP routers in an interactive fashion built
using the Autobahn-Python library.ContentsInstallationUsageStarting the REPLConnectionsSessionsCalls and InvocationsRegistrationsPublishers and PublicationsSubscriptionsExtendingPtPython config moduleREPL class substitutionREPL APIRoadmapCreditsInstallationpip installautobahn-python-replAPR requires Python 3.6 to run. If you are not using Python
3.6 in your WAMP project then it is recommend you create a Python 3.6 virtual
environment and install the REPL there.UsageStarting the REPLRun theautobahn_python_replscript installed by this packageRunpython-mopendna.autobahn.repl.replConnectionsOnce the REPL has started you will be presented with a standard PtPython prompt
and environment. In order to begin connecting to a WAMP router enter:>>> my_connection = connect_to(uri='ws://HOST:PORT', realm='MY_REALM')
Generating connection to MY_REALM@ws://HOST:PORT with name g9jZlZehYou will see thatconnect_togenerated an internal name for the connection.
You can access the connection via this internal name by entering:>>> connections.g9jZlZeh
<opendna.autobahn.repl.connections.Connection object at 0x6fc2901ab0f0>It is also possible to provide a custom internal name for the connection when
you callconnect_toas follows:>>> connect_to(uri='ws://HOST:PORT', realm='MY_REALM', name='my_connection')
Generating connection to MY_REALM@ws://HOST:PORT with name my_connectionYou can now access the connection by entering:>>> connections.my_connection
<opendna.autobahn.repl.connections.Connection object at 0x2ac690dab0f0>
>>> connections['my_connection']
<opendna.autobahn.repl.connections.Connection object at 0x2ac690dab0f0>Note that theConnectionobject is not actually a concrete connection to
the WAMP router, it is merely a storage container for connection related
details that is used to createSessionobjects which represent actual
connections to the WAMP router.connect_toaccepts the follows arguments:uri: Required. A WAMP router URI stringrealm: Optional. A WAMP realm stringextra: Optional. A dictionary of data to be supplied to the WAMPApplicationSession.``__init__`` method. Not useful unless you are
working with a customApplicationSessionsclass. SeeExtendingfor
more details on this.serializer: Optional. A list of WAMP serializers to use. Serializers must
implementautobahn.wamp.interfaces.ISerializerssl: Optional. Boolean orssl.SSLContenxtinstance. Can usually
be ignored unless you are planning to connect use TLS authentication for aSessionproxy: Optional. A dictionary providing details for a proxy server. Must
havehostandportkeysname: Optional. A name for the connectionSessionsOnce you have aConnectioninstance you can use it to create aSessioninstance, opening a WAMP session in the process:>>> my_session = my_connection.session()
Generating anonymous session to MY_REALM@ws://HOST:PORT with name bKP5ajz0You can access this session via its auto-generated name like so:>>> my_connection.sessions.bKP5ajz0
<opendna.autobahn.repl.sessions.Session object at 0x14c2b01a40fd>
>>> my_connection.sessions['bKP5ajz0']
<opendna.autobahn.repl.sessions.Session object at 0x14c2b01a40fd>sessionalso accepts anameparameter that you can use to avoid using an
auto-generated name.By default callingsessionwill open aWAMP-Anonymoussession with the router.It is also possible to specify the authentication method or methods that will
be used:>>> ticket_session = my_connection.session('ticket', authid='your_authid', ticket='YOUR_AUTHENTICATION_TICKET')
Generating ticket session to MY_REALM@ws://HOST:PORT with name SOME_NAME
>>> mixed_session = my_connection.session(['ticket', 'anonymous'], authid='your_authid', ticket='YOUR_AUTHENTICATION_TICKET')
Generating ['ticket', 'anonymous'] session to MY_REALM@ws://HOST:PORT with name SOME_OTHER_NAMEticket_sessionwill use WAMP-Ticket authentication only whilemixed_sessionwill try WAMP-Ticket first before falling back to WAMP-Anonymous.While WAMP provides a number a authentication methods, only four of are handled
at the session level (as opposed to the transport level). Calling thesessionmethod with a specific authentication method may imply the use of certain additional
parameters. These are detailed below:WAMP-Anonymous: No parameters required. Note thatauthidwill be ignored if it is suppliedWAMP-Ticket:authidandticketparameters requiredWAMP-CRA:authidandsecretparameters requiredWAMP-Cryptosign:authidandkeyparameters required.keyneeds to be an instance ofautobahn.wamp.cryptosign.SigningKeyTheConnection.sessionmethod accepts the following arguments:authmethods: Optional. String or list of strings. Valid authentication method
strings are:anonymous,ticket,wampcra,cryptosign,cookieandtlsauthid: String. Optional for WAMP-Anonymous authentication, required for all other methodsauthrole: String. Optional. Requested roleauthextra: Dictionary. Optional. Data to be passed along to the authenticator. Useful
for providing additional data to a dynamic authenticatorresumable: Boolean. Optional. Should the session be resumed later if it disconnectsresume_session: Integer. Optional. ID of Session to resumeresume_token: String. Optional. Token for resuming session specified byresume_sessionCalls and InvocationsIn order to perform WAMP RPC calls you need to create aCallinstance. This is
done using aSessioninstance:>>> my_call = my_session.call('endpoint_uri')
Generating a call to endpoint endpoint_uri with name i9BcEagWYou can access this call by it’s autogenerated name like so:>>> my_session.calls.i9BcEagW
<opendna.autobahn.repl.rpc.Call object at 0xa452bd1a6f2>
>>> my_session.calls['i9BcEagW']
<opendna.autobahn.repl.rpc.Call object at 0xa452bd1a6f2>callalso accepts a customnameparameter to bypass the use of an autogenerated
name. Furthermore, thecallmethod accepts any keyword-arguments you can
supply to theautobahn.wamp.types.CallOptions constructor.ACallinstance is itself callable and can be invoked in order to produce anInvocationinstance. Creating anInvocationinitiates the process of
sending the WAMP RPC call using theSessioninstance associated with theCallinstance that is the parent of theInvocation:>>> my_invocation = my_call(True, False, parm3=None, parm4={'something': 'or other'})
Invoking endpoint_uri with name Wax3JdBx
Invocation of endpoint_uri with name Wax3JdBx starting
Invocation of endpoint_uri with name Wax3JdBx succeededDepending on how long it takes for the remote end-point to execute, the message
indicating success or failure may not appear immediately. You will note that
theInvocationalso receives a auto-generated name which can be used to access
it from theCallinstance like so:>>> my_call.invocations.Wax3JdBx
<opendna.autobahn.repl.rpc.Invocation object at 0xd456bc1aef5>
>>> my_call.invocations['Wax3JdBx']
<opendna.autobahn.repl.rpc.Invocation object at 0xd456bc1aef5>TheInvocationinstance exposes three important properties that can be
used to access the results of the WAMP Call:resultwill contain the result of the WAMP Call if it succeeded orNoneif it failed or hasn’t completed yetexceptionwill contain the result of the WAMP Call if it failed orNoneif it succeeded or hasn’t completed yetprogressis a list which is used to store progressive results if the
target WAMP end-point emits them. Seehttps://crossbar.io/docs/Progressive-Call-Results/for more details on thisFinally, anInvocationinstance is itself callable. Calling anInvocationwill
produce a newInvocationinstance attached to the parentCallof the calledInvocation.
The behaviour of the arguments and keyword arguments when calling anInvocationis quite specific
and affects the creation of the newInvocationas follows:Positional arguments will replace the corresponding positional arguments from the parentInvocationin the newInvocationunless the positional argument is a reference to the singleton objectopendna.autobahn.repl.utils.KeepTo illustrate this consider the following input scenario:>>>  my_call = my_session.call('some_endpoint')
>>>  invocation1 = my_call(1,2,3)
>>>  invocation2 = invocation1(3, Keep, 1)
>>>  invocation3 = my_call(3,2,1)In this scenarioinvocation2andinvocation3are identicalIf the number of positional arguments supplied is less than was supplied to the parentInvocationthen the
missing positional arguments will be substituted in from the parentInvocationas ifKeephad been used in their
positionsIf the number of position arguments supplied is greater than was supplied to the parentInvocationthen the
additional positional arguments will be ignoredAny keyword arguments will replace the corresponding keyword arguments from the parentInvocation:>>> my_call = my_session.call('some_endpoint')
>>> invocation1 = my_call(x=True, y=False)
>>> invocation2 = invocation1(y=True)
>>> invocation3 = my_call(x=True, y=True)In this scenarioinvocation2andinvocation3are identicalRegistrationsIn order to handle calls to WAMP RPC end-points you need to create aRegistrationinstance:>>> my_registration = my_session.register('endpoint_uri')
Generating registration for endpoint_uri with name Rx3mmt2e
Registration of endpoint_uri with name Rx3mmt2e starting
Registration of endpoint_uri with name Rx3mmt2e succeededYou can access this registration by it’s autogenerated name like so:>>> my_session.registrations.Rx3mmt2e
<opendna.autobahn.repl.rpc.Registration object at 0x7fc89015b0f0>
>>> my_session.registrations['Rx3mmt2e']
<opendna.autobahn.repl.rpc.Registration object at 0x7fc89015b0f0>You can also provide a a customnameparameter to bypass the use of an autogenerated
name. Furthermore, theregistermethod accepts any keyword-arguments you can
supply to theautobahn.wamp.types.RegisterOptions constructor.Once a registration has succeeded it is available for calling as described in
theCalls and Invocationssection. By default theRegistrationclass
provides a default handler for incoming calls which records the input parameters
along with the date and time of the call using a aRegistration..Hitinstance.
ThisHitis anamedtupleproviding three attributes:timestamp,argsandkwargs. When the registration is the target of a call the console will output text like:End-pointendpoint_uri named Rx3mmt2e hit at2017-12-0122:04:10.030438. Hit named jqD8TxFp storedHits stored on a registration can be accessed using either the auto-generated name
or via a numeric index (hits are stored in the order they are received):>>> my_registration.hits[0]
Hit(timestamp=datetime.datetime(2017, 12, 1, 22, 4, 10, 30438), args=(1, 2, 3, False, True, {}), kwargs={'x': None})
>>> my_registration.hits.jqD8TxFp
Hit(timestamp=datetime.datetime(2017, 12, 1, 22, 4, 10, 30438), args=(1, 2, 3, False, True, {}), kwargs={'x': None})When creating aRegistrationit is also possible to specify a custom handler
which is used in addition to the default handler for incoming calls. This custom
handler may be either a standard function or an async function and is called
after the hit is stored by theRegistrationinstance. Additionally, the result
of the custom handler will be returned to the caller (the default handler will returnNonein the event that no custom handler is supplied):>>> import asyncio
>>> async def test(*args, **kwargs):
        await asyncio.sleep(5)
        print(args, kwargs)
        return True
>>> my_registration = my_session.register('endpoint_uri', test)
Generating registration for endpoint_uri with name Rx3mmt2e
Registration of endpoint_uri with name Rx3mmt2e starting
Registration of endpoint_uri with name Rx3mmt2e succeeded
>>> invocation = my_session.call('endpoint_uri')(1,2,3,False,True,{},x=None)
Generating call to endpoint_uri with name shejtoeU
Invoking endpoint_uri with name dgSHC77i
Invocation of endpoint_uri with name dgSHC77i starting
End-point endpoint_uri named Rx3mmt2e hit at 2017-12-01 22:04:10.030438. Hit named jqD8TxFp stored
(1, 2, 3, False, True, {}) {'x': None}
Invocation of endpoint_uri with name dgSHC77i succeeded
>>> invocation.result
TrueIt is also possible to deregister an existing registration:>>> my_registration.deregister()
Deregistration of endpoint_uri with name Rx3mmt2e starting
Deregistration of endpoint_uri with name Rx3mmt2e succeededPublishers and PublicationsIn order to emit WAMP PubSub events you need to create aPublisherinstance:>>> my_publisher = my_session.publish('topic_uri')
Generating publisher for topic_uri with name YunLGYwrYou can access this publisher by it’s autogenerated name like so:>>> my_session.publishers.YunLGYwr
<opendna.autobahn.repl.pubsub.Publisher object at 0x7fe1ec20a160>
>>> my_session.publishers['YunLGYwr']
<opendna.autobahn.repl.pubsub.Publisher object at 0x7fe1ec20a160>You can also provide a a customnameparameter to bypass the use of an autogenerated
name. Furthermore, thepublishmethod accepts any keyword-arguments you can
supply to theautobahn.wamp.types.PublishOptions constructor.APublisherinstance is itself callable and can be invoked in order to produce anPublicationinstance. Creating aPublicationinitiates the process of
sending the WAMP PubSub event using theSessioninstance associated with thePublisherinstance that is the parent of thePublication:>>> my_publication = my_publisher(a=True, b=False)
Publication to topic_uri with name CHrYRIn8 starting
Publication to topic_uri with name CHrYRIn8 succeededYou will note that thePublicationalso receives a auto-generated name which
can be used to access it from the parentPublisherinstance like so:>>> my_publisher.publications.CHrYRIn8
<opendna.autobahn.repl.pubsub.Publication object at 0x7fe1f496a5c0>
>>> my_publisher.publications['CHrYRIn8']
<opendna.autobahn.repl.pubsub.Publication object at 0x7fe1f496a5c0>ThePublicationinstance exposes two important properties that can be
used to access the results of the WAMP PubSub event emission:resultwill contain the result of the WAMP PubSub event emission if theacknowledgeboolean
parameter supplied to thepublishwas set toTrue. In all other instances it will containNoneexceptionwill contain the exception result of the WAMP PubSub event emission if it failed orNoneif no failure was detectedFinally, aPublicationinstance is itself callable. Calling aPublicationwill
produce a newPublicationinstance attached to the parentPublisherof the
calledPublication. The behaviour of the arguments and keyword arguments when
calling aPublicationis quite specific and affects the creation of the newPublicationas follows:Positional arguments will replace the corresponding positional arguments from the parentPublicationin the newPublicationunless the positional argument is a reference to the singleton objectopendna.autobahn.repl.utils.KeepTo illustrate this consider the following input scenario:>>>  my_publisher = my_session.publish('some_topic')
>>>  publication1 = my_publisher(1,2,3)
>>>  publication2 = publication1(3, Keep, 1)
>>>  publication3 = my_publisher(3,2,1)In this scenariopublication2andpublication3are identicalIf the number of positional arguments supplied is less than was supplied to the parentPublicationthen the
missing positional arguments will be substituted in from the parentPublicationas ifKeephad been used in their
positionsIf the number of position arguments supplied is greater than was supplied to the parentPublicationthen the
additional positional arguments will be ignoredAny keyword arguments will replace the corresponding keyword arguments from the parentPublication:>>> my_publisher = my_session.publish('some_topic')
>>> publication1 = my_publisher(x=True, y=False)
>>> publication2 = publication1(y=True)
>>> publication3 = my_publisher(x=True, y=True)In this scenariopublication2andpublication3are identicalSubscriptionsIn order to subscribe to WAMP PubSub topics you need to create aSubscriptioninstance:>>> my_subscription = my_session.subscribe('topic_uri')
Generating subscription for topic_uri with name bIMq6XcO
Subscription to topic_uri with name bIMq6XcO starting
Subscription to topic_uri with name bIMq6XcO succeededYou can access this subscription by it’s autogenerated name like so:>>> my_session.subscriptions.bIMq6XcO
<opendna.autobahn.repl.pubsub.Subscription object at 0x7fe1f5f9aef0>
>>> my_session.subscriptions['bIMq6XcO']
<opendna.autobahn.repl.pubsub.Subscription object at 0x7fe1f5f9aef0>You can also provide a a customnameparameter to bypass the use of an autogenerated
name. Furthermore, thesubscribemethod accepts any keyword-arguments you can
supply to theautobahn.wamp.types.SubscribeOptions constructor.Once a subscription has succeeded it will be notified of WAMP PubSub events
emitted as described in thePublishers and Publicationssection. Note, however,
that by default a subscription to a topic will only receive events emitted by
other sessions. Theexclude_meparameter for thePublishermust be set toTrueif you wish to test publication and subscription to a given topic within
a singleSession.TheSubscriptionclass provides a default handler for incoming events which
records the input parameters along with the date and time of the call using aSubscription.Hitinstance. ThisEventis anamedtupleproviding three
attributes:timestamp,argsandkwargs. When the subscription receives an
event the console will output text like:Event named s3X0Sbhc received at2017-12-0321:59:55.437068 on topic topic_uri named bIMq6XcOEvents stored on a subscription can be accessed using either the auto-generated name
or via a numeric index (hits are stored in the order they are received):>>> my_subscription.events[0]
Event(timestamp=datetime.datetime(2017, 12, 1, 22, 4, 10, 30438), args=(1, 2, 3, False, True, {}), kwargs={'x': None})
>>> my_subscription.events.jqD8TxFp
Event(timestamp=datetime.datetime(2017, 12, 1, 22, 4, 10, 30438), args=(1, 2, 3, False, True, {}), kwargs={'x': None})When creating aSubscriptionit is also possible to specify a custom handler
which is used in addition to the default handler for incoming events. This custom
handler may be either a standard function or an async function and is called
after the event is stored by theSubscriptioninstance:>>> async def test(*args, **kwargs):
        print(args, kwargs)
>>> my_subscription = my_session.subscribe('topic_uri', test)
Generating subscription for topic_uri with name bIMq6XcO
Subscription to topic_uri with name bIMq6XcO starting
Subscription to topic_uri with name bIMq6XcO succeeded
>>> publication = my_session.publish('topic_uri', exclude_me=False)(1,2,3,False,True,{},x=None)
Generating publisher for topic_uri with name VVjZjvF5
Publication to topic_uri with name sjfuAGSm starting
Publication to topic_uri with name sjfuAGSm succeeded
Event named ZbbzBrxJ received at 2017-12-03 22:18:10.383218 on topic topic_uri named bIMq6XcO
(1, 2, 3, False, True, {}) {'x': None}
>>> my_subscription.events.ZbbzBrxJ
Event(timestamp=datetime.datetime(2017, 12, 3, 22, 18, 10, 383218), args=(1, 2, 3, False, True, {}), kwargs={'x': None})It is also possible to unsubscribe from a topic:>>> my_subscription.unsubscribe()
Unsubscription from topic_uri with name bIMq6XcO starting
Unsubscription from topic_uri with name bIMq6XcO succeededExtendingTBDPtPython config moduleTBDREPL class substitutionTBDREPL APITBDRoadmapImproved UI with custom panes/tabs/views for examining Calls, Invocations,
Publishers, Publications, Registrations and Subscriptionsderegister/Unsubscribeshould clean up theRegistration/SubscriptioninstanceSupport usage in other REPLsYou tell me!CreditsAutobahn-Python for providing the secret WAMP saucePtPython for providing the secret REPL sauceJedi for providing PtPython with the secret code completion saucePromptToolkit for providing PtPython with the prompt secret sauce