Testapplication with python autobahn and asyncio

354 views
Skip to first unread message

Ulrikop

unread,
Jul 19, 2014, 9:29:55 AM7/19/14
to autob...@googlegroups.com
Hi,
I have got a problem with Autobahn under Python which makes me become desperate and I hope I will find help here.
I’m supposed to write a Python test program which tests the websocketconnection of a mainprogram to make sure that the desired functionality is accurate.
I got a test framework on hand which opens the main program anew for each single test so that I always got the same test environment. However this means that I have to open the connection anew before each test.
Then I would need a blocking receive with a timeout so I could say „Now something is supposed to come“.
For the websocket connection I am supposed to use Autobahn with asyncio.

My tries yet:

To make asyncio work you need to somehow make the event-loop run. Thats why I thought I’d let all test run in a subroutine and await it with run-until-complete so that I subsequently can stop the test program. Inside of the loop I afterwards open a new connection for each test.
I can’t use the code of the examples anymore:
coro = loop.create_connection(factory, '127.0.0.1', 9000)
loop
.run_until_complete(coro)


A run_until_complete musn’t be called inbetween an event loop. The consideration „ the program is already in the loop anyway so I don’t need an extra call anymore“ isn’t productive either. Because there is a lot to do in the tests Autobahn has to wait his turn pretty long. That’s why as soon as there’s the time I get an error message that the handshake didn’t work out.

I tried in several different ways now but nothing would work out. But that might be because I haven’t seen through asyncio completely yet.

My second idea: I know threads! I let Autobahn run in a thread, the queue is threadsafe anyway, so the communication is clear.

from autobahn.asyncio.websocket import WebSocketClientProtocol, \
                                     
WebSocketClientFactory
import asyncio
from queue import Queue
from _thread import start_new_thread

class MyClientProtocol(WebSocketClientProtocol):
   
def onOpen(self):
       
self.factory.client = self

   
def onMessage(self, payload, isBinary):
       queue
.put(payload.decode('utf8'))

   
def onClose(self, wasClean, code, reason):
       
self.factory.client = None

class MockFactory(WebSocketClientFactory):

   
def __init__(self, uri, queue, loop):
       
self.client = None
       
self.queue = queue

       
WebSocketClientFactory.__init__(self, uri, loop=loop, debug=False)


class WebSocketClientMock:
   
def __init__(self):
       
self.queue = Queue()
       
self.loop = asyncio.get_event_loop()
   
self.factory = MockFactory("ws://127.0.0.1:9000“, self.queue, self.loop)
       self.factory.protocol = MyClientProtocol

   def websocket_thread(self):
       coro = self.loop.create_connection(self.factory, '127.0.0.1', 9000)
       self.loop.run_until_complete(coro)
       self.loop.run_forever()
       self.loop.close()

   def connect(self):
       start_new_thread(self.websocket_thread, ())

   def close(self):
       if self.factory.client is not None:
           self.factory.client.sendClose()
           self.factory.client = None

       with self.queue.mutex:
           self.queue.queue.clear()

   def receive(self, timeout=2):
       return self.queue.get(True, timeout)



This is the latest version of my numerous tries. For example with new_event_loop in the thread ( get_event_loop doesn’t work because I always get the error message „There is no current event loop in thread 'Dummy-2‘.“)
Or I also tried to not build up the thread in connect each time but to do it once in _init_. The method websocket_thread remained as in the example, just the carrying out of create_connection was done in the method connect. But this didn’t work out.

The current version doesn’t work either, but gives the following error message:
File "/usr/lib/python3.3/site-packages/autobahn/asyncio/websocket.py", line 54, in connection_made
   
self._consume()
 
File "/usr/lib/python3.3/site-packages/autobahn/asyncio/websocket.py", line 72, in _consume
   
self.waiter = Future()
 
File "/usr/lib/python3.3/site-packages/asyncio/futures.py", line 148, in __init__
   
self._loop = events.get_event_loop()
 
File "/usr/lib/python3.3/site-packages/asyncio/events.py", line 465, in get_event_loop
   
return get_event_loop_policy().get_event_loop()
 
File "/usr/lib/python3.3/site-packages/asyncio/events.py", line 413, in get_event_loop
   threading
.current_thread().name)
AssertionError: There is no current event loop in thread 'Dummy-2‘.



I always give the loop to the factory-constructor but internally a feature without the loop gets opened. Thats is why the feature tries again to get the loop by executing the function get_event_loop.

That you very much for your support.

Ulrikop

Tobias Oberstein

unread,
Jul 21, 2014, 8:28:55 AM7/21/14
to autob...@googlegroups.com
Hi,

Am 19.07.2014 15:29, schrieb Ulrikop:
> Hi,
> I have got a problem with Autobahn under Python which makes me become
> desperate and I hope I will find help here.
> I’m supposed to write a Python test program which tests the
> websocketconnection of a mainprogram to make sure that the desired
> functionality is accurate.
> I got a test framework on hand which opens the main program anew for
> each single test so that I always got the same test environment. However
> this means that I have to open the connection anew before each test.

Understood. So you want your test driver open new WebSocket connections
to the testee - for each test a complete new connection.

Of course this is possible with both Twisted and asyncio.

For example, with Twisted, here is how Autobahn testsuite does this

https://github.com/tavendo/AutobahnTestSuite/blob/master/autobahntestsuite/autobahntestsuite/fuzzing.py#L1249

For asyncio, you will need to call `loop.create_connection` again and
again for each new connection.

E.g. in the connection close handler, fire up a new connection or do
nothing (which means the program will end).

> Then I would need a blocking receive with a timeout so I could say „Now
> something is supposed to come“.

The way you approach that in asynchronous frameworks like Twisted or
asyncio is not by doing a "blocking read" (which simply doesn't exist),
but instead:

- setup a timer
- buffer anything received
- when "enough" has been received, cancel the timer
- when the timer fires, not enough data has been received, so handle that

> For the websocket connection I am supposed to use Autobahn with asyncio.
>
> My tries yet:
>
> To make asyncio work you need to somehow make the event-loop run. Thats
> why I thought I’d let all test run in a subroutine and await it with
> run-until-complete so that I subsequently can stop the test program.
> Inside of the loop I afterwards open a new connection for each test.
> I can’t use the code of the examples anymore:
> |
> coro =loop.create_connection(factory,'127.0.0.1',9000)
> loop.run_until_complete(coro)
> |
>
>
> A run_until_complete musn’t be called inbetween an event loop. The
> consideration „ the program is already in the loop anyway so I don’t
> need an extra call anymore“ isn’t productive either. Because there is a
> lot to do in the tests Autobahn has to wait his turn pretty long. That’s
> why as soon as there’s the time I get an error message that the
> handshake didn’t work out.

The way all those asynch frameworks work is this:

1) setup/wire all your asynch logic
2) start the event loop

2) is done exactly once in a program, and this call will only return
when the whole program is finished. And the program is finished when
there are no future things to be done anymore.

>
> I tried in several different ways now but nothing would work out. But
> that might be because I haven’t seen through asyncio completely yet.

If you are completely new to asynchronous network programming (that is
you are coming from synch/blocking/threads), there are new paradigms no
learn. This isn't just throwing in some library to use. It's a complete
style of programming. Expect a phase of confusion until "you see the light".

>
> My second idea: I know threads! I let Autobahn run in a thread, the
> queue is threadsafe anyway, so the communication is clear.

I wouldn't go that road. Threads are evil. Threads are unneeded (in this
case definitely).

> This is the latest version of my numerous tries. For example with
> new_event_loop in the thread ( get_event_loop doesn’t work because I
> always get the error message „There is no current event loop in thread
> 'Dummy-2‘.“)

You must setup and start a new event loop on the new thread. Initially,
your program will only have one on the main thread. But as said, I
wouldn't encourage using threads.

> I always give the loop to the factory-constructor but internally a
> feature without the loop gets opened. Thats is why the feature tries
> again to get the loop by executing the function get_event_loop.
>
> That you very much for your support.

I'm a bit under time pressure .. otherwise I would just add another
example right away. Anyway, hoe above already helps a little. As your
questions are more of general nature (not Autobahn specific), this is
the asynchio/tulip list:
https://groups.google.com/forum/#!forum/python-tulip

People are quite helpful there as well ..

Cheers,
/Tobias

>
> Ulrikop
>
> --
> You received this message because you are subscribed to the Google
> Groups "Autobahn" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to autobahnws+...@googlegroups.com
> <mailto:autobahnws+...@googlegroups.com>.
> To post to this group, send email to autob...@googlegroups.com
> <mailto:autob...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/autobahnws/d05939b6-2fe9-47c6-8154-edcbe9601d28%40googlegroups.com
> <https://groups.google.com/d/msgid/autobahnws/d05939b6-2fe9-47c6-8154-edcbe9601d28%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages