[whomp-ee]
This is a Python implementation of WAMP not requiring Twisted or asyncio, enabling use within classic blocking Python applications. It is a light-weight alternative to autobahn.
With wampy you can quickly and easily create your own WAMP clients, whether this is in a web app, a microservice, a script or just in a Python shell.
wampy tries to provide an intuitive API for your WAMP messaging.
See ReadTheDocs for more detailed documentation.
Please be aware that I am (gratefully) receieving ongoing feedback on the API, and whilst it has been mostly stable, we have made some improvements which are breaking changes. On the 1.0 release there will be no more unexpected tweaks out of the blue to the public API. See the release notes for all changes.
- Remote Procedure Calls over websockets
- Publish and Subscribe over websockets
- Client Authentication
- Transport Layer Security
- CLI for easy and rapid development
- Pytest fixtures to use when testing your projects
- nameko integration with nameko_wamp
If you've already got access to a running Router which has other Peers connected, then stay here. If not, jump to the next section. If you're still here...
pip install wampy
...and then open a Python shell.
The example here assumes a Peer connected to a Router on localhost
, port 8080
, that has registered a remote procedure called get_foobar
, and you want to call that procedure.
from wampy.peers import Client
with Client() as client:
response = client.rpc.get_foobar()
# do something with the response here
The same example here, but the Router is on a remote host.
from wampy.peers import Client
with Client(url="ws://example.com:8080") as client:
response = client.rpc.get_foobar()
# do something with the response here
The WAMP Session is "context managed", meaning it begins as you enter, and ends as you exit the scope of the client instance.
See ReadTheDocs for much more detail on this.
Before any messaging can happen you need a Router. Messages are then routed between Clients over an administrative domain on the Router called a Realm.
For the quickest of starts I suggest that you use Crossbar.io and start it up on the default host and port, and with the default realm and roles. See the Crossbar.io docs for the instructions on this or alternatively run with wampy's testing setup.
$ pip install --editable .[dev]
$ crossbar start --config ./wampy/testing/configs/crossbar.json
Now open your preferred text editor and we'll write a few lines of Python constructing a simple WAMP service that takes a decimal number and returns the binary representation of it - wowzers!
from wampy.peers.clients import Client
from wampy.roles import callee
class BinaryNumberService(Client):
@callee
def get_binary_number(self, number):
return bin(number)
Save this module somewhere on your Python path and we'll use a wampy command line interface tool to start the service.
$ wampy run path.to.your.module.including.module_name:BinaryNumberService
For example, running one of the wampy example applications against the Router suggested previously:
$ wampy run docs.examples.services:DateService --config ./wampy/testing/configs/crossbar.json
Actually - no need to panic! The BinaryNumberService
example already exists in the wampy examples so put that text editor away if you like. Just execute from the command line:
$ wampy run docs.examples.services:BinaryNumberService --config ./wampy/testing/configs/crossbar.json
Now, open a Python console in a new terminal, allowing the BinaryNumberService
to run uninterupted in your original terminal (but once you're done with it Ctrl-C
is required).
In [1]: from wampy.peers.clients import Client
In [2]: with Client(url="ws://localhost:8080") as client:
result = client.rpc.get_binary_number(number=100)
In [3]: result
Out[3]: u'0b1100100'
The RPC pattern above was inspired by the nameko project, but this pattern may not feel intuitive for those familiar with Crossbar.io, the primary Router used by wampy.
For this reason there also exists the CallProxy
object which implements the call
API by more loosely wrapping wampy's Call
Message. In this pattern, applications and their endpoints are identified by dot delimented strings rather than a single API name, e.g.
"com.example.endpoint"
Just like the rpc
API, the call
API is directly available on every wampy client. Lets look at the two examples side by side.
>>> client.rpc.get_foo_bar(eggs, foo=bar, spam=ham)
>>> client.call("get_foo_bar", eggs, foo=bar, spam=ham)
Noted these are very similar and achieve the same, but the intention here is for the call
API to behave more like a classic Crossbar.io application and the rpc
to be used in nameko_wamp.
The call
API however does allow calls of the form...
>>> client.call("com.myapp.foo.bar", eggs, foo=bar, spam=ham)
...which you will not be able to do with the rpc
API.
To demonstrate, first of all you need a Subscriber. You can either create one yourself in a Python module (as a subclass of a wampy Client
) or use the example Client
already for you in docs.examples.services
.
Here we use the said example service, but all a Subscriber is is a wampy Client
with a method decorated by subscribe
. Take a look and see for yourself in the examples.
Let's start up that example service.
$ wampy run docs.examples.services:SubscribingService --config ./wampy/testing/configs/crossbar.json
Now we have a service running that subscribes to the topic "foo".
In another terminal, with a wampy virtualenv, you can create a Publisher - which is no different to any other wampy Client.
In [1]: from wampy.peers import Client
In [2]: with Client() as client:
result = client.publish(topic="foo", message="spam")
Hopefully you'll see any message you send printed to the screen where the example service is running. You'll also see the meta data that wampy chooses to send.
See ReadTheDocs for more detailed documentation.
$ pip install --editable .[dev]
$ py.test ./test -v
$ pip install -r rtd_requirements.txt
$ sphinx-build -E -b html ./docs/ ./docs/_build/
If you like this project, then Thank You, and you're welcome to get involved.