diff --git a/autobahn/autobahn/asyncio/wamp.py b/autobahn/autobahn/asyncio/wamp.py index 27ce11ee..6a888f00 100644 --- a/autobahn/autobahn/asyncio/wamp.py +++ b/autobahn/autobahn/asyncio/wamp.py @@ -72,7 +72,7 @@ class FutureMixin: return future.add_done_callback(done) def _gather_futures(self, futures, consume_exceptions = True): - return asyncio.gather(futures, return_exception = consume_exceptions) + return asyncio.gather(*futures, return_exceptions = consume_exceptions) diff --git a/autobahn/autobahn/wamp/protocol.py b/autobahn/autobahn/wamp/protocol.py index 99b493fe..ad683893 100644 --- a/autobahn/autobahn/wamp/protocol.py +++ b/autobahn/autobahn/wamp/protocol.py @@ -189,7 +189,7 @@ class BaseSession: if isinstance(exc, exception.ApplicationError): msg = message.Error(request_type, request, six.u(exc.error), args = list(exc.args), kwargs = exc.kwargs) else: - if self._ecls_to_uri_pat.has_key(exc.__class__): + if exc.__class__ in self._ecls_to_uri_pat: error = self._ecls_to_uri_pat[exc.__class__][0]._uri else: error = u"wamp.error.runtime_error" @@ -219,7 +219,7 @@ class BaseSession: exc = None - if self._uri_to_ecls.has_key(msg.error): + if msg.error in self._uri_to_ecls: ecls = self._uri_to_ecls[msg.error] try: ## the following might fail, eg. TypeError when @@ -575,7 +575,11 @@ class ApplicationSession(BaseSession): log.err(err) del self._invocations[msg.request] - reply = self._message_from_exception(message.Invocation.MESSAGE_TYPE, msg.request, err.value) + if hasattr(err, 'value'): + exc = err.value + else: + exc = err + reply = self._message_from_exception(message.Invocation.MESSAGE_TYPE, msg.request, exc) self._transport.send(reply) self._invocations[msg.request] = d @@ -881,7 +885,8 @@ class ApplicationSession(BaseSession): ## decorated with "wamp.procedure" ## dl = [] - for k in inspect.getmembers(endpoint.__class__, inspect.ismethod): +# for k in inspect.getmembers(endpoint.__class__, inspect.ismethod): + for k in inspect.getmembers(endpoint.__class__, inspect.isfunction): proc = k[1] if "_wampuris" in proc.__dict__: pat = proc.__dict__["_wampuris"][0] diff --git a/autobahn/autobahn/wamp/types.py b/autobahn/autobahn/wamp/types.py index b6b8849f..ba3775d4 100644 --- a/autobahn/autobahn/wamp/types.py +++ b/autobahn/autobahn/wamp/types.py @@ -18,6 +18,8 @@ from __future__ import absolute_import +import six + class ComponentConfig: def __init__(self, realm = None, extra = None): @@ -49,7 +51,7 @@ class Accept(HelloReturn): class Deny(HelloReturn): - def __init__(self, reason = "wamp.error.not_authorized", message = None): + def __init__(self, reason = u"wamp.error.not_authorized", message = None): self.reason = reason self.message = message @@ -160,7 +162,7 @@ class PublishOptions: discloseMe = None): """ Constructor. - + :param acknowledge: If True, acknowledge the publication with a success or error response. :type acknowledge: bool @@ -177,8 +179,8 @@ class PublishOptions: """ assert(acknowledge is None or type(acknowledge) == bool) assert(excludeMe is None or type(excludeMe) == bool) - assert(exclude is None or (type(exclude) == list and all(type(x) in [int, long] for x in exclude))) - assert(eligible is None or (type(eligible) == list and all(type(x) in [int, long] for x in eligible))) + assert(exclude is None or (type(exclude) == list and all(type(x) in six.integer_types for x in exclude))) + assert(eligible is None or (type(eligible) == list and all(type(x) in six.integer_types for x in eligible))) assert(discloseMe is None or type(discloseMe) == bool) self.options = { @@ -205,7 +207,7 @@ class RegisterOptions: in this keyword argument to the callable. :type details_arg: str """ - assert(details_arg is None or type(details_arg) == str) + assert(details_arg is None or type(details_arg) == six.text_type) self.details_arg = details_arg self.options = { 'pkeys': pkeys, @@ -274,9 +276,9 @@ class CallOptions: :type runOn: str """ assert(onProgress is None or callable(onProgress)) - assert(timeout is None or (type(timeout) in [int, float] and timeout > 0)) + assert(timeout is None or (type(timeout) in six.integer_types + [float] and timeout > 0)) assert(discloseMe is None or type(discloseMe) == bool) - assert(runOn is None or (type(runOn) == str and runOn in ["all", "any", "partition"])) + assert(runOn is None or (type(runOn) == six.text_type and runOn in [u"all", u"any", u"partition"])) self.options = { 'timeout': timeout, diff --git a/examples/asyncio/wamp/basic/.gitignore b/examples/asyncio/wamp/basic/.gitignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/examples/asyncio/wamp/basic/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/examples/asyncio/wamp/basic/Makefile b/examples/asyncio/wamp/basic/Makefile new file mode 100644 index 00000000..73f516a4 --- /dev/null +++ b/examples/asyncio/wamp/basic/Makefile @@ -0,0 +1,224 @@ +all: + @echo "Targets:" + @echo "" + @echo "Router-only:" + @echo " server" + @echo "" + @echo "RPC Time Service:" + @echo " server_with_rpc_timeservice_backend" + @echo " client_rpc_timeservice_frontend" + @echo " client_rpc_timeservice_backend" + @echo "" + @echo "RPC Slow Square:" + @echo " server_with_rpc_slowsquare_backend" + @echo " client_rpc_slowsquare_frontend" + @echo " client_rpc_slowsquare_backend" + @echo "" + @echo "RPC Arguments:" + @echo " server_with_rpc_arguments_backend" + @echo " client_rpc_arguments_frontend" + @echo " client_rpc_arguments_backend" + @echo "" + @echo "RPC Options:" + @echo " server_with_rpc_options_backend" + @echo " client_rpc_options_frontend" + @echo " client_rpc_options_backend" + @echo "" + @echo "RPC Errors:" + @echo " server_with_rpc_errors_backend" + @echo " client_rpc_errors_frontend" + @echo " client_rpc_errors_backend" + @echo "" + @echo "RPC Complex Result:" + @echo " server_with_rpc_complex_backend" + @echo " client_rpc_complex_frontend" + @echo " client_rpc_complex_backend" + @echo "" + @echo "RPC Progressive Results:" + @echo " server_with_rpc_progress_backend" + @echo " client_rpc_progress_frontend" + @echo " client_rpc_progress_backend" + @echo "" + @echo "RPC Decorators:" + @echo " server_with_rpc_decorators_backend" + @echo " client_rpc_decorators_frontend" + @echo " client_rpc_decorators_backend" + @echo "" + @echo "PubSub Basic:" + @echo " server_with_pubsub_basic_backend" + @echo " client_pubsub_basic_frontend" + @echo " client_pubsub_basic_backend" + @echo "" + @echo "PubSub Complex Event:" + @echo " server_with_pubsub_complex_backend" + @echo " client_pubsub_complex_frontend" + @echo " client_pubsub_complex_backend" + @echo "" + @echo "PubSub Options:" + @echo " server_with_pubsub_options_backend" + @echo " client_pubsub_options_frontend" + @echo " client_pubsub_options_backend" + @echo "" + @echo "PubSub Unsubscribe:" + @echo " server_with_pubsub_unsubscribe_backend" + @echo " client_pubsub_unsubscribe_frontend" + @echo " client_pubsub_unsubscribe_backend" + @echo "" + @echo "PubSub Decorators:" + @echo " server_with_pubsub_decorators_backend" + @echo " client_pubsub_decorators_frontend" + @echo " client_pubsub_decorators_backend" + @echo "" + @echo "Session Series:" + @echo " server_with_session_series_backend" + @echo " client_session_series_frontend" + @echo " client_session_series_backend" + @echo "" + + +s: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py + + +s1: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "rpc.timeservice.backend.Component" + +f1: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.timeservice.frontend.Component" + +b1: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.timeservice.backend.Component" + + +s2: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "rpc.slowsquare.backend.Component" + +f2: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.slowsquare.frontend.Component" + +b2: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.slowsquare.backend.Component" + + +s3: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "rpc.arguments.backend.Component" + +f3: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.arguments.frontend.Component" + +b3: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.arguments.backend.Component" + + +s4: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "rpc.options.backend.Component" + +f4: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.options.frontend.Component" + +b4: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.options.backend.Component" + + +s5: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "rpc.errors.backend.Component" + +f5: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.errors.frontend.Component" + +b5: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.errors.backend.Component" + + +s6: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "rpc.complex.backend.Component" + +f6: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.complex.frontend.Component" + +b6: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.complex.backend.Component" + + +s7: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "rpc.progress.backend.Component" + +f7: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.progress.frontend.Component" + +b7: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.progress.backend.Component" + + +s8: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "rpc.decorators.backend.Component" + +f8: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.decorators.frontend.Component" + +b8: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "rpc.decorators.backend.Component" + + +s9: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "pubsub.basic.backend.Component" + +f9: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.basic.frontend.Component" + +b9: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.basic.backend.Component" + + +s10: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "pubsub.complex.backend.Component" + +f10: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.complex.frontend.Component" + +b10: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.complex.backend.Component" + + +s11: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "pubsub.options.backend.Component" + +f11: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.options.frontend.Component" + +b11: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.options.backend.Component" + + +s12: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "pubsub.unsubscribe.backend.Component" + +f12: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.unsubscribe.frontend.Component" + +b12: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.unsubscribe.backend.Component" + + +s13: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "pubsub.decorators.backend.Component" + +f13: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.decorators.frontend.Component" + +b13: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "pubsub.decorators.backend.Component" + + +s14: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 server.py --component "session.series.backend.Component" + +f14: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "session.series.frontend.Component" + +b14: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 client.py --component "session.series.backend.Component" + + +client_session_fromoutside_backend: + PYTHONPATH=../../../../autobahn ~/python34/bin/python3 session/fromoutside/backend.py diff --git a/examples/asyncio/wamp/basic/README.md b/examples/asyncio/wamp/basic/README.md new file mode 100644 index 00000000..4b7aa41a --- /dev/null +++ b/examples/asyncio/wamp/basic/README.md @@ -0,0 +1,206 @@ +# WAMP v2 Examples + +The examples in this folder serve to illustrate **[WAMP version 2](https://github.com/tavendo/WAMP/blob/master/spec/README.md)** on [**Autobahn**|Python](http://autobahn.ws/): + +* WAMP **RPC** and **PubSub** features for application use +* example WAMP **application components** and **routers** + +# Application Component Deployment + +**[WAMP v2](https://github.com/tavendo/WAMP/blob/master/spec/README.md)** on [**Autobahn**|Python](http://autobahn.ws/) allows to run application components in different deployment configurations without any changes to application code: + +![Application Code Deployment Options](figures/app_code_depl_options.png) + +## Running the Demos + +All demos use the same two example application routers to host the application components for a demo: + + * [A WAMP/WebSocket server container](server.py) + * [A WAMP/WebSocket client container](client.py) + +The application components of the demos are separate from the example application routres, and each application component demonstrates a different RPC or PubSub feature. + +### Router with embedded application backend component + +Run the example application router on a WebSocket transport server and start a demo "backend" application component inside the router: + + python server.py --component "rpc.timeservice.backend.Component" + +Run the demo "frontend" application component over a WebSocket transport client: + + python client.py --component "rpc.timeservice.frontend.Component" + + +### Application backend component in client + +Run the example application router on a WebSocket transport server: + + python server.py + +Run the demo "backend" application component over a WebSocket transport client: + + python client.py --component "rpc.timeservice.backend.Component" + +Run the demo "frontend" application component over a WebSocket transport client: + + python client.py --component "rpc.timeservice.frontend.Component" + + +### Other Transports + +To start a server accepting WAMP connections on TCP port 8080 using a **rawsocket** transport with MsgPack serialization: + +```shell +python server.py --debug --endpoint "tcp:8080" --transport "rawsocket-msgpack" \ + --component "rpc.timeservice.backend.Component" +``` + +To start a client connecting to this server: + +```shell +python client.py --endpoint "tcp:127.0.0.1:8080" --transport "rawsocket-msgpack" \ + --component "rpc.timeservice.frontend.Component" +``` + + +## Available Demos + +### Remote Procedure Calls + +#### Time Service + +A trivial time service - demonstrates basic remote procedure feature. + + * `rpc.timeservice.backend.Component` + * `rpc.timeservice.frontend.Component` + +#### Slow Square + +Demonstrates procedures which return promises and return asynchronously. + + * `rpc.slowsquare.backend.Component` + * `rpc.slowsquare.frontend.Component` + +#### Arguments + +Demonstrates all variants of call arguments. + + * `rpc.arguments.backend.Component` + * `rpc.arguments.frontend.Component` + +#### Complex Result + +Demonstrates complex call results (call results with more than one positional or keyword results). + + * `rpc.complex.backend.Component` + * `rpc.complex.frontend.Component` + +#### Errors + +Demonstrates error raising and catching over remote procedures. + + * `rpc.errors.backend.Component` + * `rpc.errors.frontend.Component` + +#### Progressive Results + +Demonstrates calling remote procedures that produce progressive results. + + * `rpc.progress.backend.Component` + * `rpc.progress.frontend.Component` + +#### Options + +Using options with RPC. + + * `rpc.options.backend.Component` + * `rpc.options.backend.Component` + + +### Publish & Subscribe + +#### Time Service + +Demonstrates basic publish and subscribe. + + * `pubsub.basic.backend.Component` + * `pubsub.basic.frontend.Component` + +#### Complex Events + +Demonstrates publish and subscribe with complex events. + + * `pubsub.complex.backend.Component` + * `pubsub.complex.frontend.Component` + +#### Options + +Using options with PubSub. + + * `pubsub.options.backend.Component` + * `pubsub.options.frontend.Component` + +#### Unsubscribing + +Shows how to unsubscribe. + + * `pubsub.unsubscribe.backend.Component` + * `pubsub.unsubscribe.frontend.Component` + + +### Session + +#### Session Series + +Demonstrates how multiple sessions can exist during the lifetime of the underlying transport. + + * `session.series.backend.Component` + * `session.series.frontend.Component` + + +## AutobahnJS-based Demos + +In addition, the demo front- and backends are available as AutobahnJS-based versions to run in browsers and NodeJS. + +For example, run the example application router on a WebSocket transport server and start a demo "backend" application component inside the router: + + python server.py --component "rpc.timeservice.backend.Component" + +Then, open the JavaScript frontend in a browser: + + rpc/timeservice_frontend.html + +To run the frontend from NodeJS, install AutobahnJS + + npm install autobahn + npm install when + +and then + + node rpc/timeservice_frontend.js + +To run the backend in NodeJS, run the plain router + + python server.py + +and then start the backend: + + node rpc/timeservice_backend.js + +### Deployment Options + +Application frontend in browser, application backend in browser: + +![Application Code Deployment Options](figures/timeservice1.png) + +Application frontend in browser, application backend in NodeJS: + +![Application Code Deployment Options](figures/timeservice2.png) + +Application frontend in browser, application backend in Python: + +![Application Code Deployment Options](figures/timeservice3.png) + +Application frontend in browser, application backend in Python (Router embedded): + +![Application Code Deployment Options](figures/timeservice4.png) diff --git a/examples/asyncio/wamp/basic/client.py b/examples/asyncio/wamp/basic/client.py new file mode 100644 index 00000000..d5b17a1a --- /dev/null +++ b/examples/asyncio/wamp/basic/client.py @@ -0,0 +1,100 @@ +############################################################################### +## +## Copyright (C) 2011-2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + + +if __name__ == '__main__': + + import sys, argparse, asyncio + + + ## parse command line arguments + ## + parser = argparse.ArgumentParser() + + parser.add_argument("-d", "--debug", action = "store_true", + help = "Enable debug output.") + + parser.add_argument("-c", "--component", type = str, + help = "Start WAMP client with this application component, e.g. 'timeservice.TimeServiceFrontend'") + + parser.add_argument("--host", type = str, default = "127.0.0.1", + help = 'IP or hostname to connect to.') + + parser.add_argument("--port", type = int, default = 8080, + help = 'TCP port to connect to.') + + parser.add_argument("--transport", choices = ['websocket', 'rawsocket-json', 'rawsocket-msgpack'], default = "websocket", + help = 'WAMP transport type') + + parser.add_argument("--url", type = str, default = None, + help = 'The WebSocket URL to connect to, e.g. ws://127.0.0.1:8080/ws.') + + args = parser.parse_args() + + + ## create a WAMP application session factory + ## + from autobahn.asyncio.wamp import ApplicationSessionFactory + session_factory = ApplicationSessionFactory() + + + ## dynamically load the application component .. + ## + import importlib + c = args.component.split('.') + mod, klass = '.'.join(c[:-1]), c[-1] + app = importlib.import_module(mod) + + ## .. and set the session class on the factory + ## + session_factory.session = getattr(app, klass) + + + if args.transport == "websocket": + + ## create a WAMP-over-WebSocket transport client factory + ## + from autobahn.asyncio.websocket import WampWebSocketClientFactory + transport_factory = WampWebSocketClientFactory(session_factory, url = args.url, debug_wamp = True) + transport_factory.setProtocolOptions(failByDrop = False) + + elif args.transport in ['rawsocket-json', 'rawsocket-msgpack']: + + ## create a WAMP-over-RawSocket transport client factory + ## + if args.transport == 'rawsocket-msgpack': + from autobahn.wamp.serializer import MsgPackSerializer + serializer = MsgPackSerializer() + elif args.transport == 'rawsocket-json': + from autobahn.wamp.serializer import JsonSerializer + serializer = JsonSerializer() + else: + raise Exception("should not arrive here") + + from autobahn.asyncio.rawsocket import WampRawSocketClientFactory + transport_factory = WampRawSocketClientFactory(session_factory, serializer, debug = args.debug) + + + ## start the client + loop = asyncio.get_event_loop() + coro = loop.create_connection(transport_factory, args.host, args.port) + loop.run_until_complete(coro) + + ## now enter the asyncio event loop + loop.run_forever() + loop.close() diff --git a/examples/asyncio/wamp/basic/pubsub/__init__.py b/examples/asyncio/wamp/basic/pubsub/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/pubsub/basic/__init__.py b/examples/asyncio/wamp/basic/pubsub/basic/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/basic/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/pubsub/basic/backend.py b/examples/asyncio/wamp/basic/pubsub/basic/backend.py new file mode 100644 index 00000000..0029cb3b --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/basic/backend.py @@ -0,0 +1,47 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that publishes an event every second. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + @inlineCallbacks + def onJoin(self, details): + counter = 0 + while True: + self.publish('com.myapp.topic1', counter) + counter += 1 + yield sleep(1) diff --git a/examples/asyncio/wamp/basic/pubsub/basic/frontend.py b/examples/asyncio/wamp/basic/pubsub/basic/frontend.py new file mode 100644 index 00000000..8bc7e333 --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/basic/frontend.py @@ -0,0 +1,55 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that subscribes and receives events, + and stop after having received 5 events. + """ + + def onConnect(self): + self.join("realm1") + + + @inlineCallbacks + def onJoin(self, details): + + self.received = 0 + + def on_event(i): + print("Got event: {}".format(i)) + self.received += 1 + if self.received > 5: + self.leave() + + yield self.subscribe(on_event, 'com.myapp.topic1') + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + reactor.stop() diff --git a/examples/asyncio/wamp/basic/pubsub/complex/__init__.py b/examples/asyncio/wamp/basic/pubsub/complex/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/complex/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/pubsub/complex/backend.py b/examples/asyncio/wamp/basic/pubsub/complex/backend.py new file mode 100644 index 00000000..11caff47 --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/complex/backend.py @@ -0,0 +1,56 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import random + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.wamp.types import SubscribeOptions +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that publishes events with no payload + and with complex payloads every second. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + @inlineCallbacks + def onJoin(self, details): + + counter = 0 + while True: + self.publish('com.myapp.heartbeat') + + obj = {'counter': counter, 'foo': [1, 2, 3]} + self.publish('com.myapp.topic2', random.randint(0, 100), 23, c = "Hello", d = obj) + + counter += 1 + yield sleep(1) diff --git a/examples/asyncio/wamp/basic/pubsub/complex/frontend.py b/examples/asyncio/wamp/basic/pubsub/complex/frontend.py new file mode 100644 index 00000000..f42e769d --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/complex/frontend.py @@ -0,0 +1,65 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import random + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.wamp.types import SubscribeOptions +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that subscribes and receives events + of no payload and of complex payload, and stops after 5 seconds. + """ + + def onConnect(self): + self.join("realm1") + + + @inlineCallbacks + def onJoin(self, details): + + self.received = 0 + + def on_heartbeat(details = None): + print("Got heartbeat (publication ID {})".format(details.publication)) + + yield self.subscribe(on_heartbeat, 'com.myapp.heartbeat', options = SubscribeOptions(details_arg = 'details')) + + + def on_topic2(a, b, c = None, d = None): + print("Got event: {} {} {} {}".format(a, b, c, d)) + + yield self.subscribe(on_topic2, 'com.myapp.topic2') + + + reactor.callLater(5, self.leave) + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + reactor.stop() diff --git a/examples/asyncio/wamp/basic/pubsub/decorators/__init__.py b/examples/asyncio/wamp/basic/pubsub/decorators/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/decorators/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/pubsub/decorators/backend.py b/examples/asyncio/wamp/basic/pubsub/decorators/backend.py new file mode 100644 index 00000000..fc854d03 --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/decorators/backend.py @@ -0,0 +1,48 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that publishes an event every second. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + @inlineCallbacks + def onJoin(self, details): + counter = 0 + while True: + self.publish('com.myapp.topic1', counter) + self.publish('com.myapp.topic2', "Hello world.") + counter += 1 + yield sleep(1) diff --git a/examples/asyncio/wamp/basic/pubsub/decorators/frontend.py b/examples/asyncio/wamp/basic/pubsub/decorators/frontend.py new file mode 100644 index 00000000..79b0f702 --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/decorators/frontend.py @@ -0,0 +1,77 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn import wamp +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that subscribes and receives events, + and stop after having received 5 events. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + self.received = 0 + + + def onConnect(self): + self.join(self._realm) + + + @inlineCallbacks + def onJoin(self, details): + + ## subscribe all methods on this object decorated with "@wamp.topic" + ## as PubSub event handlers + ## + results = yield self.subscribe(self) + for success, res in results: + if success: + ## res is an Subscription instance + print("Ok, subscribed handler with subscription ID {}".format(res.id)) + else: + ## res is an Failure instance + print("Failed to subscribe handler: {}".format(res.value)) + + + @wamp.topic('com.myapp.topic1') + def onEvent1(self, i): + print("Got event on topic1: {}".format(i)) + self.received += 1 + if self.received > 5: + self.leave() + + + @wamp.topic('com.myapp.topic2') + def onEvent2(self, msg): + print("Got event on topic2: {}".format(msg)) + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + reactor.stop() diff --git a/examples/asyncio/wamp/basic/pubsub/options/__init__.py b/examples/asyncio/wamp/basic/pubsub/options/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/options/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/pubsub/options/backend.py b/examples/asyncio/wamp/basic/pubsub/options/backend.py new file mode 100644 index 00000000..cd9ed6ac --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/options/backend.py @@ -0,0 +1,57 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.wamp.types import PublishOptions, EventDetails, SubscribeOptions +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that publishes an event every second. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + @inlineCallbacks + def onJoin(self, details): + + def on_event(i): + print("Got event: {}".format(i)) + + yield self.subscribe(on_event, 'com.myapp.topic1') + + + counter = 0 + while True: + publication = yield self.publish('com.myapp.topic1', counter, + options = PublishOptions(acknowledge = True, discloseMe = True, excludeMe = False)) + print("Event published with publication ID {}".format(publication.id)) + counter += 1 + yield sleep(1) diff --git a/examples/asyncio/wamp/basic/pubsub/options/frontend.py b/examples/asyncio/wamp/basic/pubsub/options/frontend.py new file mode 100644 index 00000000..be6ab1f3 --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/options/frontend.py @@ -0,0 +1,58 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.wamp.types import PublishOptions, EventDetails, SubscribeOptions +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that subscribes and receives events, + and stop after having received 5 events. + """ + + def onConnect(self): + self.join("realm1") + + + @inlineCallbacks + def onJoin(self, details): + + self.received = 0 + + def on_event(i, details = None): + print("Got event, publication ID {}, publisher {}: {}".format(details.publication, details.publisher, i)) + self.received += 1 + if self.received > 5: + self.leave() + + yield self.subscribe(on_event, 'com.myapp.topic1', + options = SubscribeOptions(details_arg = 'details')) + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + reactor.stop() diff --git a/examples/asyncio/wamp/basic/pubsub/unsubscribe/__init__.py b/examples/asyncio/wamp/basic/pubsub/unsubscribe/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/unsubscribe/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/pubsub/unsubscribe/backend.py b/examples/asyncio/wamp/basic/pubsub/unsubscribe/backend.py new file mode 100644 index 00000000..e45a0208 --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/unsubscribe/backend.py @@ -0,0 +1,48 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that publishes an event every second. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + @inlineCallbacks + def onJoin(self, details): + + counter = 0 + while True: + self.publish('com.myapp.topic1', counter) + counter += 1 + yield sleep(1) diff --git a/examples/asyncio/wamp/basic/pubsub/unsubscribe/frontend.py b/examples/asyncio/wamp/basic/pubsub/unsubscribe/frontend.py new file mode 100644 index 00000000..8b637eae --- /dev/null +++ b/examples/asyncio/wamp/basic/pubsub/unsubscribe/frontend.py @@ -0,0 +1,72 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component that subscribes and receives events. + After receiving 5 events, it unsubscribes, sleeps and then + resubscribes for another run. Then it stops. + """ + + @inlineCallbacks + def test(self): + + self.received = 0 + + @inlineCallbacks + def on_event(i): + print("Got event: {}".format(i)) + self.received += 1 + if self.received > 5: + self.runs += 1 + if self.runs > 1: + self.leave() + else: + yield self.subscription.unsubscribe() + print("Unsubscribed .. continue in 2s ..") + reactor.callLater(2, self.test) + + self.subscription = yield self.subscribe(on_event, 'com.myapp.topic1') + print("Subscribed with subscription ID {}".format(self.subscription.id)) + + + def onConnect(self): + self.join("realm1") + + + @inlineCallbacks + def onJoin(self, details): + + self.runs = 0 + yield self.test() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + reactor.stop() diff --git a/examples/asyncio/wamp/basic/rpc/__init__.py b/examples/asyncio/wamp/basic/rpc/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/arguments/__init__.py b/examples/asyncio/wamp/basic/rpc/arguments/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/arguments/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/arguments/backend.py b/examples/asyncio/wamp/basic/rpc/arguments/backend.py new file mode 100644 index 00000000..6d6bd136 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/arguments/backend.py @@ -0,0 +1,53 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component providing procedures with different kinds of arguments. + """ + + def onConnect(self): + self.join(u"realm1") + + + def onJoin(self, details): + + def ping(): + return + + def add2(a, b): + return a + b + + def stars(nick = "somebody", stars = 0): + return u"{} starred {}x".format(nick, stars) + + def orders(product, limit = 5): + return [u"Product {}".format(i) for i in range(50)][:limit] + + def arglen(*args, **kwargs): + return [len(args), len(kwargs)] + + self.register(ping, u'com.arguments.ping') + self.register(add2, u'com.arguments.add2') + self.register(stars, u'com.arguments.stars') + self.register(orders, u'com.arguments.orders') + self.register(arglen, u'com.arguments.arglen') diff --git a/examples/asyncio/wamp/basic/rpc/arguments/frontend.py b/examples/asyncio/wamp/basic/rpc/arguments/frontend.py new file mode 100644 index 00000000..91811847 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/arguments/frontend.py @@ -0,0 +1,81 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component calling the different backend procedures. + """ + + def onConnect(self): + self.join(u"realm1") + + + @asyncio.coroutine + def onJoin(self, details): + + yield from self.call(u'com.arguments.ping') + print("Pinged!") + + res = yield from self.call(u'com.arguments.add2', 2, 3) + print("Add2: {}".format(res)) + + starred = yield from self.call(u'com.arguments.stars') + print("Starred 1: {}".format(starred)) + + starred = yield from self.call(u'com.arguments.stars', nick = u'Homer') + print("Starred 2: {}".format(starred)) + + starred = yield from self.call(u'com.arguments.stars', stars = 5) + print("Starred 3: {}".format(starred)) + + starred = yield from self.call(u'com.arguments.stars', nick = u'Homer', stars = 5) + print("Starred 4: {}".format(starred)) + + orders = yield from self.call(u'com.arguments.orders', u'coffee') + print("Orders 1: {}".format(orders)) + + orders = yield from self.call(u'com.arguments.orders', u'coffee', limit = 10) + print("Orders 2: {}".format(orders)) + + arglengths = yield from self.call(u'com.arguments.arglen') + print("Arglen 1: {}".format(arglengths)) + + arglengths = yield from self.call(u'com.arguments.arglen', 1, 2, 3) + print("Arglen 1: {}".format(arglengths)) + + arglengths = yield from self.call(u'com.arguments.arglen', a = 1, b = 2, c = 3) + print("Arglen 2: {}".format(arglengths)) + + arglengths = yield from self.call(u'com.arguments.arglen', 1, 2, 3, a = 1, b = 2, c = 3) + print("Arglen 3: {}".format(arglengths)) + + self.leave() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + asyncio.get_event_loop().stop() diff --git a/examples/asyncio/wamp/basic/rpc/complex/__init__.py b/examples/asyncio/wamp/basic/rpc/complex/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/complex/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/complex/backend.py b/examples/asyncio/wamp/basic/rpc/complex/backend.py new file mode 100644 index 00000000..c4a8dc4b --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/complex/backend.py @@ -0,0 +1,52 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.wamp.types import CallResult +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + Application component that provides procedures which + return complex results. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + def onJoin(self, details): + + def add_complex(a, ai, b, bi): + return CallResult(c = a + b, ci = ai + bi) + + self.register(add_complex, 'com.myapp.add_complex') + + def split_name(fullname): + forename, surname = fullname.split() + return CallResult(forename, surname) + + self.register(split_name, 'com.myapp.split_name') diff --git a/examples/asyncio/wamp/basic/rpc/complex/frontend.py b/examples/asyncio/wamp/basic/rpc/complex/frontend.py new file mode 100644 index 00000000..18215dfd --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/complex/frontend.py @@ -0,0 +1,53 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.wamp.types import CallResult +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + Application component that calls procedures which + produce complex results and showing how to access those. + """ + + def onConnect(self): + self.join("realm1") + + + @asyncio.coroutine + def onJoin(self, details): + + res = yield from self.call('com.myapp.add_complex', 2, 3, 4, 5) + print("Result: {} + {}i".format(res.kwresults['c'], res.kwresults['ci'])) + + res = yield from self.call('com.myapp.split_name', 'Homer Simpson') + print("Forname: {}, Surname: {}".format(res.results[0], res.results[1])) + + self.leave() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + asyncio.get_event_loop().stop() diff --git a/examples/asyncio/wamp/basic/rpc/decorators/__init__.py b/examples/asyncio/wamp/basic/rpc/decorators/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/decorators/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/decorators/backend.py b/examples/asyncio/wamp/basic/rpc/decorators/backend.py new file mode 100644 index 00000000..b1ef68cb --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/decorators/backend.py @@ -0,0 +1,73 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import datetime + +import asyncio + +from autobahn import wamp +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component registering RPC endpoints using decorators. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + @asyncio.coroutine + def onJoin(self, details): + + ## register all methods on this object decorated with "@wamp.procedure" + ## as a RPC endpoint + ## + results = yield from self.register(self) + for res in results: + if isinstance(res, wamp.protocol.Registration): + ## res is an Registration instance + print("Ok, registered procedure with registration ID {}".format(res.id)) + else: + ## res is an Failure instance + print("Failed to register procedure: {}".format(res)) + + + @wamp.procedure('com.mathservice.add2') + def add2(self, x, y): + return x + y + + + @wamp.procedure('com.mathservice.mul2') + def mul2(self, x, y): + return x * y + + + @wamp.procedure('com.mathservice.div2') + def square(self, x, y): + if y: + return float(x) / float(y) + else: + return 0 diff --git a/examples/asyncio/wamp/basic/rpc/decorators/frontend.py b/examples/asyncio/wamp/basic/rpc/decorators/frontend.py new file mode 100644 index 00000000..daeed3a0 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/decorators/frontend.py @@ -0,0 +1,61 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component calling the different backend procedures. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + @asyncio.coroutine + def onJoin(self, details): + + procs = [u'com.mathservice.add2', + u'com.mathservice.mul2', + u'com.mathservice.div2'] + + try: + for proc in procs: + res = yield from self.call(proc, 2, 3) + print("{}: {}".format(proc, res)) + except Exception as e: + print("Something went wrong: {}".format(e)) + + self.leave() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + asyncio.get_event_loop().stop() diff --git a/examples/asyncio/wamp/basic/rpc/errors/__init__.py b/examples/asyncio/wamp/basic/rpc/errors/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/errors/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/errors/backend.py b/examples/asyncio/wamp/basic/rpc/errors/backend.py new file mode 100644 index 00000000..07e78a56 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/errors/backend.py @@ -0,0 +1,91 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import math + +import asyncio + +from autobahn import wamp +from autobahn.wamp.exception import ApplicationError +from autobahn.asyncio.wamp import ApplicationSession + + + +@wamp.error("com.myapp.error1") +class AppError1(Exception): + """ + An application specific exception that is decorated with a WAMP URI, + and hence can be automapped by Autobahn. + """ + + + +class Component(ApplicationSession): + """ + Example WAMP application backend that raised exceptions. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + def onJoin(self, details): + + ## raising standard exceptions + ## + def sqrt(x): + if x == 0: + raise Exception("don't ask folly questions;)") + else: + ## this also will raise, if x < 0 + return math.sqrt(x) + + self.register(sqrt, 'com.myapp.sqrt') + + + ## raising WAMP application exceptions + ## + def checkname(name): + if name in ['foo', 'bar']: + raise ApplicationError("com.myapp.error.reserved") + + if name.lower() != name.upper(): + ## forward positional arguments in exceptions + raise ApplicationError("com.myapp.error.mixed_case", name.lower(), name.upper()) + + if len(name) < 3 or len(name) > 10: + ## forward keyword arguments in exceptions + raise ApplicationError("com.myapp.error.invalid_length", min = 3, max = 10) + + self.register(checkname, 'com.myapp.checkname') + + + ## defining and automapping WAMP application exceptions + ## + self.define(AppError1) + + def compare(a, b): + if a < b: + raise AppError1(b - a) + + self.register(compare, 'com.myapp.compare') diff --git a/examples/asyncio/wamp/basic/rpc/errors/frontend.py b/examples/asyncio/wamp/basic/rpc/errors/frontend.py new file mode 100644 index 00000000..4b63fad3 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/errors/frontend.py @@ -0,0 +1,90 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import math + +import asyncio + +from autobahn import wamp +from autobahn.wamp.exception import ApplicationError +from autobahn.asyncio.wamp import ApplicationSession + + + +@wamp.error("com.myapp.error1") +class AppError1(Exception): + """ + An application specific exception that is decorated with a WAMP URI, + and hence can be automapped by Autobahn. + """ + + + +class Component(ApplicationSession): + """ + Example WAMP application frontend that catches exceptions. + """ + + def onConnect(self): + self.join("realm1") + + + @asyncio.coroutine + def onJoin(self, details): + + ## catching standard exceptions + ## + for x in [2, 0, -2]: + try: + res = yield from self.call('com.myapp.sqrt', x) + except Exception as e: + print("Error: {} {}".format(e, e.args)) + else: + print("Result: {}".format(res)) + + + ## catching WAMP application exceptions + ## + for name in ['foo', 'a', '*'*11, 'Hello']: + try: + res = yield from self.call('com.myapp.checkname', name) + except ApplicationError as e: + print("Error: {} {} {} {}".format(e, e.error, e.args, e.kwargs)) + else: + print("Result: {}".format(res)) + + + ## defining and automapping WAMP application exceptions + ## + self.define(AppError1) + + try: + yield from self.call('com.myapp.compare', 3, 17) + except AppError1 as e: + print("Compare Error: {}".format(e)) + + + self.leave() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + asyncio.get_event_loop().stop() diff --git a/examples/asyncio/wamp/basic/rpc/options/__init__.py b/examples/asyncio/wamp/basic/rpc/options/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/options/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/options/backend.py b/examples/asyncio/wamp/basic/rpc/options/backend.py new file mode 100644 index 00000000..7d198b59 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/options/backend.py @@ -0,0 +1,56 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.wamp.types import CallOptions, RegisterOptions, PublishOptions +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component providing procedures with + different kinds of arguments. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + def onJoin(self, details): + + def square(val, details = None): + print("square called from: {}".format(details.caller)) + + if val < 0: + self.publish('com.myapp.square_on_nonpositive', val) + elif val == 0: + if details.caller: + options = PublishOptions(exclude = [details.caller]) + else: + options = None + self.publish('com.myapp.square_on_nonpositive', val, options = options) + return val * val + + self.register(square, 'com.myapp.square', RegisterOptions(details_arg = 'details')) diff --git a/examples/asyncio/wamp/basic/rpc/options/frontend.py b/examples/asyncio/wamp/basic/rpc/options/frontend.py new file mode 100644 index 00000000..187c9932 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/options/frontend.py @@ -0,0 +1,55 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.wamp.types import CallOptions, RegisterOptions, PublishOptions +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component calling the different backend procedures. + """ + + def onConnect(self): + self.join("realm1") + + + @asyncio.coroutine + def onJoin(self, details): + + def on_event(val): + print("Someone requested to square non-positive: {}".format(val)) + + yield from self.subscribe(on_event, 'com.myapp.square_on_nonpositive') + + for val in [2, 0, -2]: + res = yield from self.call('com.myapp.square', val, options = CallOptions(discloseMe = True)) + print("Squared {} = {}".format(val, res)) + + self.leave() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + asyncio.get_event_loop().stop() diff --git a/examples/asyncio/wamp/basic/rpc/progress/__init__.py b/examples/asyncio/wamp/basic/rpc/progress/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/progress/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/progress/backend.py b/examples/asyncio/wamp/basic/rpc/progress/backend.py new file mode 100644 index 00000000..6a0efabd --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/progress/backend.py @@ -0,0 +1,52 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.wamp.types import CallOptions, RegisterOptions +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + Application component that produces progressive results. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + def onJoin(self, details): + + @asyncio.coroutine + def longop(n, details = None): + if details.progress: + for i in range(n): + details.progress(i) + yield from asyncio.sleep(1) + else: + yield from asyncio.sleep(1 * n) + return n + + self.register(longop, 'com.myapp.longop', RegisterOptions(details_arg = 'details')) diff --git a/examples/asyncio/wamp/basic/rpc/progress/frontend.py b/examples/asyncio/wamp/basic/rpc/progress/frontend.py new file mode 100644 index 00000000..9b19f2f0 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/progress/frontend.py @@ -0,0 +1,53 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.wamp.types import CallOptions, RegisterOptions +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + Application component that consumes progressive results. + """ + + def onConnect(self): + self.join("realm1") + + + @asyncio.coroutine + def onJoin(self, details): + + def on_progress(i): + print("Progress: {}".format(i)) + + res = yield from self.call('com.myapp.longop', 3, options = CallOptions(onProgress = on_progress)) + + print("Final: {}".format(res)) + + self.leave() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + asyncio.get_event_loop().stop() diff --git a/examples/asyncio/wamp/basic/rpc/slowsquare/__init__.py b/examples/asyncio/wamp/basic/rpc/slowsquare/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/slowsquare/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/slowsquare/backend.py b/examples/asyncio/wamp/basic/rpc/slowsquare/backend.py new file mode 100644 index 00000000..48fbac62 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/slowsquare/backend.py @@ -0,0 +1,52 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import asyncio + +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + A math service application component. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + def onJoin(self, details): + + def square(x): + return x * x + + self.register(square, 'com.math.square') + + + @asyncio.coroutine + def slowsquare(x, delay = 1): + yield from asyncio.sleep(delay) + return x * x + + self.register(slowsquare, 'com.math.slowsquare') diff --git a/examples/asyncio/wamp/basic/rpc/slowsquare/frontend.py b/examples/asyncio/wamp/basic/rpc/slowsquare/frontend.py new file mode 100644 index 00000000..e359c35d --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/slowsquare/frontend.py @@ -0,0 +1,62 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import time +import asyncio +from functools import partial + +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component using the time service. + """ + + def onConnect(self): + self.join("realm1") + + + @asyncio.coroutine + def onJoin(self, details): + + def got(started, msg, f): + res = f.result() + duration = 1000. * (time.clock() - started) + print("{}: {} in {}".format(msg, res, duration)) + + t1 = time.clock() + d1 = self.call('com.math.slowsquare', 3) + d1.add_done_callback(partial(got, t1, "Slow Square")) + + t2 = time.clock() + d2 = self.call('com.math.square', 3) + d2.add_done_callback(partial(got, t2, "Quick Square")) + + yield from asyncio.gather(d1, d2) + print("All finished.") + self.leave() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + asyncio.get_event_loop().stop() diff --git a/examples/asyncio/wamp/basic/rpc/timeservice/__init__.py b/examples/asyncio/wamp/basic/rpc/timeservice/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/timeservice/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/rpc/timeservice/backend.py b/examples/asyncio/wamp/basic/rpc/timeservice/backend.py new file mode 100644 index 00000000..e9a35bcc --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/timeservice/backend.py @@ -0,0 +1,45 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import datetime + +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + A simple time service application component. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + def onJoin(self, details): + + def utcnow(): + now = datetime.datetime.utcnow() + return now.strftime("%Y-%m-%dT%H:%M:%SZ") + + self.register(utcnow, 'com.timeservice.now') diff --git a/examples/asyncio/wamp/basic/rpc/timeservice/frontend.py b/examples/asyncio/wamp/basic/rpc/timeservice/frontend.py new file mode 100644 index 00000000..29bca1d2 --- /dev/null +++ b/examples/asyncio/wamp/basic/rpc/timeservice/frontend.py @@ -0,0 +1,52 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import datetime +import asyncio + +from autobahn.asyncio.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component using the time service. + """ + + def onConnect(self): + self.join("realm1") + + + @asyncio.coroutine + def onJoin(self, details): + try: + now = yield from self.call('com.timeservice.now') + except Exception as e: + print("Error: {}".format(e)) + else: + print("Current time from time service: {}".format(now)) + + self.leave() + + + def onLeave(self, details): + self.disconnect() + + + def onDisconnect(self): + asyncio.get_event_loop().stop() diff --git a/examples/asyncio/wamp/basic/server.py b/examples/asyncio/wamp/basic/server.py new file mode 100644 index 00000000..c89db9a5 --- /dev/null +++ b/examples/asyncio/wamp/basic/server.py @@ -0,0 +1,117 @@ +############################################################################### +## +## Copyright (C) 2011-2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + + +if __name__ == '__main__': + + import sys, argparse, asyncio + + + ## parse command line arguments + ## + parser = argparse.ArgumentParser() + + parser.add_argument("-d", "--debug", action = "store_true", + help = "Enable debug output.") + + parser.add_argument("-c", "--component", type = str, default = None, + help = "Start WAMP server with this application component, e.g. 'timeservice.TimeServiceBackend', or None.") + + parser.add_argument("--interface", type = str, default = "127.0.0.1", + help = 'IP of interface to listen on.') + + parser.add_argument("--port", type = int, default = 8080, + help = 'TCP port to listen on.') + + parser.add_argument("--transport", choices = ['websocket', 'rawsocket-json', 'rawsocket-msgpack'], default = "websocket", + help = 'WAMP transport type') + + args = parser.parse_args() + + + ## create a WAMP router factory + ## + from autobahn.wamp.router import RouterFactory + router_factory = RouterFactory() + + + ## create a WAMP router session factory + ## + from autobahn.asyncio.wamp import RouterSessionFactory + session_factory = RouterSessionFactory(router_factory) + + + ## if asked to start an embedded application component .. + ## + if args.component: + ## dynamically load the application component .. + ## + import importlib + c = args.component.split('.') + mod, klass = '.'.join(c[:-1]), c[-1] + app = importlib.import_module(mod) + SessionKlass = getattr(app, klass) + + ## .. and create and add an WAMP application session to + ## run next to the router + ## + session_factory.add(SessionKlass()) + + + if args.transport == "websocket": + + ## create a WAMP-over-WebSocket transport server factory + ## + from autobahn.asyncio.websocket import WampWebSocketServerFactory + transport_factory = WampWebSocketServerFactory(session_factory, debug_wamp = args.debug) + transport_factory.setProtocolOptions(failByDrop = False) + + elif args.transport in ['rawsocket-json', 'rawsocket-msgpack']: + + ## create a WAMP-over-RawSocket transport server factory + ## + if args.transport == 'rawsocket-msgpack': + from autobahn.wamp.serializer import MsgPackSerializer + serializer = MsgPackSerializer() + elif args.transport == 'rawsocket-json': + from autobahn.wamp.serializer import JsonSerializer + serializer = JsonSerializer() + else: + raise Exception("should not arrive here") + + from autobahn.asyncio.rawsocket import WampRawSocketServerFactory + transport_factory = WampRawSocketServerFactory(session_factory, serializer, debug = args.debug) + + else: + raise Exception("should not arrive here") + + + ## start the server from an endpoint + ## + loop = asyncio.get_event_loop() + coro = loop.create_server(transport_factory, args.interface, args.port) + server = loop.run_until_complete(coro) + + try: + ## now enter the asyncio event loop + loop.run_forever() + except KeyboardInterrupt: + pass + finally: + server.close() + loop.close() diff --git a/examples/asyncio/wamp/basic/session/__init__.py b/examples/asyncio/wamp/basic/session/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/session/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/session/fromoutside/README.md b/examples/asyncio/wamp/basic/session/fromoutside/README.md new file mode 100644 index 00000000..6480d528 --- /dev/null +++ b/examples/asyncio/wamp/basic/session/fromoutside/README.md @@ -0,0 +1,13 @@ +This example demonstrates how to access an app session instance from outside - via the session factory. + +It runs an application component as a client connected to a WAMP router. + +Start a WAMP router: + + python ../../server.py + +Start the backend component (which will run inside a client connecting to the router): + + python client.py + +Open `frontend.html` in your browser. \ No newline at end of file diff --git a/examples/asyncio/wamp/basic/session/fromoutside/client.py b/examples/asyncio/wamp/basic/session/fromoutside/client.py new file mode 100644 index 00000000..e3aa3868 --- /dev/null +++ b/examples/asyncio/wamp/basic/session/fromoutside/client.py @@ -0,0 +1,116 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + + +from twisted.internet.defer import inlineCallbacks + +from autobahn.twisted.util import sleep +from autobahn.twisted.wamp import ApplicationSession + + + +class MyAppComponent(ApplicationSession): + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + def onConnect(self): + self.join(self._realm) + + def onJoin(self, details): + if not self.factory._myAppSession: + self.factory._myAppSession = self + + def onLeave(self, details): + if self.factory._myAppSession == self: + self.factory._myAppSession = None + + + + +if __name__ == '__main__': + + import sys + + from twisted.python import log + from twisted.internet.endpoints import clientFromString + log.startLogging(sys.stdout) + + + ## we use an Autobahn utility to import the "best" available Twisted reactor + ## + from autobahn.twisted.choosereactor import install_reactor + reactor = install_reactor() + print("Running on reactor {}".format(reactor)) + + + ## create a WAMP application session factory + ## + from autobahn.twisted.wamp import ApplicationSessionFactory + session_factory = ApplicationSessionFactory() + + + ## .. and set the session class on the factory + ## + session_factory.session = MyAppComponent + + + ## since we are running this component as a client, there + ## will be only 1 app session instance anyway. We'll store a + ## reference on the session factory, so we can access it + ## from "outside" the session instance later (see below) + ## + session_factory._myAppSession = None + + + ## create a WAMP-over-WebSocket transport client factory + ## + from autobahn.twisted.websocket import WampWebSocketClientFactory + transport_factory = WampWebSocketClientFactory(session_factory, "ws://127.0.0.1:8080/ws") + + + ## start a WebSocket client from an endpoint + ## + client = clientFromString(reactor, "tcp:127.0.0.1:8080") + client.connect(transport_factory) + + + ## publish an event every second from the (single) application session + ## that get created by the session factory + ## + @inlineCallbacks + def pub(): + counter = 0 + while True: + ## here we can access the app session that was created .. + ## + if session_factory._myAppSession: + session_factory._myAppSession.publish('com.myapp.topic123', counter) + print("published event", counter) + else: + print("no session") + counter += 1 + yield sleep(1) + + pub() + + + ## now enter the Twisted reactor loop + ## + reactor.run() diff --git a/examples/asyncio/wamp/basic/session/series/__init__.py b/examples/asyncio/wamp/basic/session/series/__init__.py new file mode 100644 index 00000000..7a5ffd2f --- /dev/null +++ b/examples/asyncio/wamp/basic/session/series/__init__.py @@ -0,0 +1,17 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### diff --git a/examples/asyncio/wamp/basic/session/series/backend.py b/examples/asyncio/wamp/basic/session/series/backend.py new file mode 100644 index 00000000..754abf4e --- /dev/null +++ b/examples/asyncio/wamp/basic/session/series/backend.py @@ -0,0 +1,48 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import datetime + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + A simple time service application component. + """ + + def __init__(self, realm = "realm1"): + ApplicationSession.__init__(self) + self._realm = realm + + + def onConnect(self): + self.join(self._realm) + + + def onJoin(self, details): + + def utcnow(): + now = datetime.datetime.utcnow() + return now.strftime("%Y-%m-%dT%H:%M:%SZ") + + self.register(utcnow, 'com.timeservice.now') diff --git a/examples/asyncio/wamp/basic/session/series/frontend.py b/examples/asyncio/wamp/basic/session/series/frontend.py new file mode 100644 index 00000000..a42bd3b5 --- /dev/null +++ b/examples/asyncio/wamp/basic/session/series/frontend.py @@ -0,0 +1,70 @@ +############################################################################### +## +## Copyright (C) 2014 Tavendo GmbH +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +############################################################################### + +import datetime + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from autobahn.twisted.wamp import ApplicationSession + + + +class Component(ApplicationSession): + """ + An application component using the time service + during 3 subsequent WAMP sessions, while the + underlying transport continues to exist. + """ + + def __init__(self): + ApplicationSession.__init__(self) + self.count = 0 + + + def onConnect(self): + print("Transport connected.") + self.join("realm1") + + + @inlineCallbacks + def onJoin(self, details): + print("Realm joined (WAMP session started).") + + try: + now = yield self.call('com.timeservice.now') + except Exception as e: + print("Error: {}".format(e)) + else: + print("Current time from time service: {}".format(now)) + + self.leave() + + + def onLeave(self, details): + print("Realm left (WAMP session ended).") + self.count += 1 + if self.count < 3: + self.join("realm1") + else: + self.disconnect() + + + def onDisconnect(self): + print("Transport disconnected.") + reactor.stop()