Skip to content

Commit

Permalink
Prepare docs for 1.0.0 release.
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinTArthur committed May 8, 2020
1 parent ba99be4 commit 67acbad
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 45 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# serverwamp Changelog
## 1.0.0
Brand new API with…
* Support for multiple realms w/ separate handlers
* Context-manager-friendly subscription handlers
* Session state handlers.
* Routes configuration for topics similar to RPC.
* Slightly more structured concurrency on the asyncio side.
* Transport authenticators, ticket authenticators, and CRA auth.

Released 2020-05-07 alongside new documentation.

## 0.2.3
Transport auth, RPC and subscriptions now working.
78 changes: 44 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,60 +8,70 @@ ASGI servers using asyncio or Trio and aiohttp servers.

### Usage Example
```python
import asyncio

from aiohttp import web
import serverwamp

docs_api = serverwamp.RPCRouteSet()
docs_events = serverwamp.TopicRouteSet()

@docs_api.route('docs.getDocument')
@docs_api.route('docs.getDoc')
async def get_doc(document_id):
record = await mydb.retrieve(document_id)
return {'status': 'SUCCESS', 'document': record}
record = await my_db.retrieve(document_id)
return {'status': 'SUCCESS', 'doc': record}

@docs_api.route('docs.deleteDocument')
@docs_api.route('docs.deleteDoc')
async def delete_doc(document_id):
succeeded = await mydb.delete(document_id)
succeeded = await my_db.delete(document_id)
if succeeded:
return {'status': 'SUCCESS'}
else:
return serverwamp.RPCErrorResult('wamp.error.delete_failed',
kwargs={'status': 'FAILURE'})

return serverwamp.RPCErrorResult(
'wamp.error.delete_failed',
kwargs={'status': 'FAILURE'}
)

@docs_events.route('docs.changes')
async def subscribe_to_doc_changes(session, changes_subscribers):
changes_subscribers.add(session)
session.send_event(args=('Thanks for subscribing!',))
yield
changes_subscribers.remove(session)


async def server_context(app):

async def watch_for_docs_changes():
async for event in my_db.watch_changes():
for session in app['changes_subscribers']:
await session.send_event(f'Document changed: {event}')

watcher = asyncio.create_task(watch_for_docs_changes())
try:
yield
finally:
watcher.cancel()


if __name__ == '__main__':
changes_subscribers = set()

app = serverwamp.Application()
app.add_rpc_routes(docs_api)
app.add_topic_routes(docs_events)
app.set_default_arg('changes_subscribers', changes_subscribers)

http_app = web.Application()
http_app.add_routes((web.get('/', app.aiohttp_websocket_handler()),))
http_app['changes_subscribers'] = changes_subscribers
http_app.add_routes((
web.get('/', app.aiohttp_websocket_handler()),
))
http_app.cleanup_ctx.append(server_context)

web.run_app(http_app)
```

## Compared to Traditional WAMP
Traditionally, WAMP clients connect to a WAMP router either to provide
functionality or to use functionality provided by other clients. This library
enables a server to respond to calls and subscriptions like a WAMP router
would, but serve them in a custom way instead of routing them to other clients.

You might do this to provide Python-based web services over a WebSocket using a
typical client and server model, while taking advantage of the structure the
WAMP protocol provides.

In summary, this is not a WAMP router, but you could use it to build one if you
wanted.

### This library is good if…
* you like developing in micro-frameworks like Flask and
aiohttp, but want to branch into serving over WebSockets.
* you want a structure for communicating over WebSockets, but want more
request/response features than the socket.io protocol provides.
* you want to build a WAMP router.
### It's not useful if…
* you want to build a WAMP client
* Consider [Autobahn|Python](https://autobahn.readthedocs.io/) instead.
* you want a WAMP router out of the box.
* Consider [Crossbar.io](https://crossbar.io/) instead.


## Development
Unit tests can be run with:
Expand Down
32 changes: 32 additions & 0 deletions docs/compared_to_traditional_wamp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Compared to Traditional WAMP
============================
In the traditional WAMP model, WAMP peers connect to a WAMP router either to
provide functionality or to use functionality provided by other peers. This can
be very useful in the *Internet of Things* where you have devices coming online
and offline with each device bringing along a unique set of services.

This library enables a server to respond to calls and subscriptions like a WAMP
router would, but serve them itself instead of routing to other clients.

You might do this to provide Python-based web services over a WebSocket using a
typical client and server model, while taking advantage of the structure the
WAMP protocol provides.


This library is good if…
------------------------
• you like developing in micro-frameworks like Flask and aiohttp, but want to
branch into serving over WebSockets.
• you want a structure for communicating over WebSockets, but want more
request/response features than socket.io or MQTT over Websockets provides.
• you want to build a WAMP router.

It's not useful if…
-------------------
• you want to build a WAMP client or peer

• Consider `Autobahn|Python <https://autobahn.readthedocs.io/>`_ instead.

• you want a WAMP router out of the box.

• Consider `Crossbar.io <https://crossbar.io/>`_ instead.
7 changes: 4 additions & 3 deletions docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,14 @@ subscription is torn down or the session is closed.
@events.topic('hourly_updates')
async def subscribe_to_hourly_updates(topic, session):
async def subscribe_to_hourly_updates(topic, session, my_subscribers):
# On subscribe…
self._to_update.add(session)
my_subscribers.add(session)
yield
# On unsubscribe or session close…
self._to_update.remove(session)
my_subscribers.remove(session)
async def run_hourly_updates():
Expand All @@ -219,6 +219,7 @@ subscription is torn down or the session is closed.
await asyncio.sleep(60.0)
app.add_topics(events)
app.add_default_arg('my_subscribers', set())
asyncio.create_task(run_hourly_updates)
Expand Down
49 changes: 42 additions & 7 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,76 @@ serverwamp Documentation
========================
Adds `Web Application Messaging Protocol <https://wamp-proto.org/>`_ features
to WebSocket servers. *serverwamp* makes it easy to both serve requests from
clients and push events to clients over the same connection. It currently
clients and push events to clients over a single connection. It currently
supports ASGI servers or aiohttp.

Example service that allows WebSocket clients to retrieve or remove documents
from a database:

.. code-block:: python
import asyncio
from aiohttp import web
import serverwamp
docs_api = serverwamp.RPCRouteSet()
docs_events = serverwamp.TopicRouteSet()
@docs_api.route('docs.getDoc')
async def get_doc(document_id):
record = await mydb.retrieve(document_id)
return {'status': 'SUCCESS', 'document': record}
record = await my_db.retrieve(document_id)
return {'status': 'SUCCESS', 'doc': record}
@docs_api.route('docs.deleteDoc')
async def delete_doc(document_id):
succeeded = await mydb.delete(document_id)
succeeded = await my_db.delete(document_id)
if succeeded:
return {'status': 'SUCCESS'}
else:
return serverwamp.RPCErrorResult('wamp.error.delete_failed',
kwargs={'status': 'FAILURE'})
return serverwamp.RPCErrorResult(
'wamp.error.delete_failed',
kwargs={'status': 'FAILURE'}
)
@docs_events.route('docs.changes')
async def subscribe_to_doc_changes(session, changes_subscribers):
changes_subscribers.add(session)
session.send_event(args=('Thanks for subscribing!',))
yield
changes_subscribers.remove(session)
async def server_context(app):
async def watch_for_docs_changes():
async for event in my_db.watch_changes():
for session in app['changes_subscribers']:
await session.send_event(f'Document changed: {event}')
watcher = asyncio.create_task(watch_for_docs_changes())
try:
yield
finally:
watcher.cancel()
if __name__ == '__main__':
changes_subscribers = set()
app = serverwamp.Application()
app.add_rpc_routes(docs_api)
app.add_topic_routes(docs_events)
app.set_default_arg('changes_subscribers', changes_subscribers)
http_app = web.Application()
http_app['changes_subscribers'] = changes_subscribers
http_app.add_routes((
web.get('/', app.aiohttp_websocket_handler()),
))
http_app.cleanup_ctx.append(server_context)
web.run_app(http_app)
.. toctree::
Expand All @@ -46,6 +80,7 @@ from a database:

getting_started
advanced_usage
compared_to_traditional_wamp



Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
),
extras_require={
'docs': (
'Sphinx==3.0.0',
'Sphinx==3.0.3',
)
},
keywords=(
Expand Down

0 comments on commit 67acbad

Please sign in to comment.