I have this 3.6 async code:
async def send(command,userPath,token):
async with websockets.connect('wss://127.0.0.1:7000',ssl=ssl.SSLContext(protocol=ssl.PROTOCOL_TLS)) as websocket:
data = json.dumps({"api_command":"session","body":command,"headers": {'X-User-Path': userPath, 'X-User-Token': token}})
await websocket.send(data)
response = await websocket.recv()
response = json.loads(response)
if 'command' in response:
if response['command'] == 'ACK_COMMAND' or response['command'] == 'ACK_INITIALIZATION':
return (response['message'],200)
else:
return(response,400)
which I converted to this 3.4 async code
#asyncio.coroutine
def send(command,userPath,token):
with websockets.connect('wss://127.0.0.1:7000',ssl=ssl.SSLContext(protocol=ssl.PROTOCOL_TLS)) as websocket:
data = json.dumps({"api_command":"session","body":command,"headers": {'X-User-Path': userPath, 'X-User-Token': token}})
yield from websocket.send(data)
response = yield from websocket.recv()
response = json.loads(response)
if 'command' in response:
if response['command'] == 'ACK_COMMAND' or response['command'] == 'ACK_INITIALIZATION':
return (response['message'],200)
else:
return(response,400)
Although the interpreter runs the conversion, when I call the function this error occurs:
with websockets.connect('wss://127.0.0.1:7000',ssl=ssl.SSLContext(protocol=ssl.PROTOCOL_TLS)) as websocket:
AttributeError: __enter__
I feel like there is more stuff to convert, but I don't know what. How can I make the 3.4 code work?
Note: I run the 3.4 code with a 3.6 python
As can be found here of async with websockets.connect you should do:
websocket = yield from websockets.connect('ws://localhost:8765/')
try:
# your stuff
finally:
yield from websocket.close()
In your case it would be:
#asyncio.coroutine
def send(command,userPath,token):
websocket = yield from websockets.connect('wss://127.0.0.1:7000',ssl=ssl.SSLContext(protocol=ssl.PROTOCOL_TLS))
try:
data = json.dumps({"api_command":"session","body":command,"headers": {'X-User-Path': userPath, 'X-User-Token': token}})
yield from websocket.send(data)
response = yield from websocket.recv()
response = json.loads(response)
if 'command' in response:
if response['command'] == 'ACK_COMMAND' or response['command'] == 'ACK_INITIALIZATION':
return (response['message'],200)
else:
return(response,400)
finally:
yield from websocket.close()
Related
Hello everyone, I'm writing tests for a game that is written in the python websockets library. When running the test test_game_started an error appears that asyncio loop is not running, but the test passes.
I provide the entire test code and the error below:
Code of HelpTest class:
class HelpTest:
def __init__(self, loop):
self.loop = loop
self.ws1 = None
self.ws2 = None
self.msg1 = None
self.msg2 = None
self.tasks = []
async def listen1(self):
async with websockets.connect(f"ws://localhost:27000") as websocket:
self.ws1 = websocket
while True:
try:
self.msg1 = await websocket.recv()
await asyncio.sleep(0.1)
except RuntimeError:
break
async def listen2(self):
async with websockets.connect(f"ws://localhost:27000") as websocket:
self.ws2 = websocket
while True:
try:
self.msg2 = await websocket.recv()
await asyncio.sleep(0.1)
except RuntimeError:
break
async def in_game_helper(self, mapData1, mapData2):
self.tasks.extend([self.loop.create_task(self.listen1()), self.loop.create_task(self.listen2())])
await asyncio.wait(self.tasks)
await asyncio.sleep(1)
await self.ws1.send(generate_payload("knock-knock", {"nick": 'a'}))
await asyncio.sleep(0.4)
await self.ws2.send(generate_payload("knock-knock", {"nick": 'b'}))
await asyncio.sleep(0.4)
await self.ws1.send(json.dumps({"header": "send_map", 'data': mapData1}))
await asyncio.sleep(0.4)
await self.ws1.send(json.dumps({"header": "send_map", 'data': mapData2}))
await asyncio.sleep(3)
#for task in self.tasks:
# print(task)
# task.cancel()
#self.loop.stop()
return json.loads(self.msg1)
async def kill_helper(self, coords):
pass
async def miss_helper(self, coords):
pass
Code of TestServer class with tests:
class TestServer:
# main()
def setup_class(self):
self.loop = asyncio.new_event_loop()
self.ws1 = None
self.ws2 = None
self.msg1 = None
self.msg2 = None
self.tasks = []
#pytest.fixture
def mapData1(self):
f = open("map1.json", 'r').read()
data = json.loads(f)
return data
#pytest.fixture
def mapData2(self):
f = open("map2.json", 'r').read()
data = json.loads(f)
return data
#pytest.fixture
def event_loop(self):
loop = asyncio.get_event_loop()
yield loop
loop.close()
#pytest.mark.asyncio
async def test_knock(self):
async with websockets.connect(f"ws://localhost:27000") as websocket:
await websocket.send(generate_payload("knock-knock", {"nick": 'a'}))
for i in range(3):
msg = json.loads(await websocket.recv())
await asyncio.sleep(0.5)
assert msg['header'] == 'registered'
#pytest.mark.asyncio
async def test_send1(self, mapData1):
async with websockets.connect(f"ws://localhost:27000") as websocket:
await websocket.send(generate_payload("knock-knock", {"nick": 'a'}))
for i in range(3):
await websocket.recv()
await asyncio.sleep(0.5)
await websocket.send(json.dumps({"header": "send_map", 'data': mapData1}))
msg = json.loads(await websocket.recv())
assert msg['header'] == 'ready'
await websocket.close()
#pytest.mark.asyncio
async def test_send2(self, mapData2):
async with websockets.connect(f"ws://localhost:27000") as websocket:
await websocket.send(generate_payload("knock-knock", {"nick": 'b'}))
for i in range(4):
await websocket.recv()
await asyncio.sleep(0.5)
await websocket.send(json.dumps({"header": "send_map", 'data': mapData2}))
msg = json.loads(await websocket.recv())
assert msg['header'] == 'ready'
await websocket.close()
#pytest.mark.asyncio
async def test_game_started(self, mapData1, mapData2, event_loop):
helper = HelpTest(event_loop)
answer = await helper.in_game_helper(mapData1, mapData2)
print(answer)
assert answer['header'] == "in_game!!!"
Traceback of runned test:
Traceback (most recent call last):
File "tests.py", line 40, in listen2
break
File "SeaBattle\venv\lib\site-packages\websockets\legacy\client.py", line 650, in __aexit__
await self.protocol.close()
File "SeaBattle\venv\lib\site-packages\websockets\legacy\protocol.py", line 768, in close
await asyncio.wait_for(
File "C:\Users\zayyc\AppData\Local\Programs\Python\Python39\lib\asyncio\tasks.py", line 435, in wait_for
loop = events.get_running_loop()
RuntimeError: no running event loop
I've tried to use pytest,mark.asyncio loop, but it did not help. I think the problem is that the tasks listen1() and listen2() keep running after the loop is closed, I tried to stop them, but the error still persists. I will be very happy with your decisions.
Basically, what it does, is to do 20 requests async to google.
If I launch it without using PyTest, just a snip of code, like this, it works:
import asyncio
import aiohttp
async def get(
session: aiohttp.ClientSession,
) -> dict:
url = f"https://www.google.com/"
resp = await session.request('GET', url=url)
data = await resp.json()
return data
async def sessions():
async with aiohttp.ClientSession() as session:
tasks = []
for i in range(20):
tasks.append(get(session=session))
return await asyncio.gather(*tasks, return_exceptions=True)
def main():
loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(loop)
htmls = loop.run_until_complete(sessions())
finally:
loop.close()
print(htmls)
But when I use PyTest, in spite of being the same code (almost), the "htmls" variable at the end is not assignated any value
import aiohttp
import asyncio
async def get(
session: aiohttp.ClientSession,
) -> dict:
url = f"https://www.google.com/"
resp = await session.request('GET', url=url)
data = await resp.json()
return data
async def sessions(self):
async with aiohttp.ClientSession() as session:
tasks = []
for i in range(20):
tasks.append(self.get(session=session))
return await asyncio.gather(*tasks, return_exceptions=True)
def test_example(self):
loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(loop)
htmls = loop.run_until_complete(self.sessions())
finally:
loop.close()
print(htmls)
Why is this? It is like loop.run_until_complete(self.sessions()) is not waiting for it to finish.
It is resolved. It needed a self as first parameter for the get() method :S
I have an websocket server up and running fine using FastAPI.
However, when i am using those "await", i am getting some latency issues. At first, i though this had something to do with internet connection, or perhaps my linux server. But it appears to be that asyncio waits for other tasks.
Here is my code:
import asyncio
from pydantic import BaseModel
class UserClientWebSocket(BaseModel):
id: str
ws: WebSocket
class Config:
arbitrary_types_allowed = True
class ConnectionManager:
def __init__(self):
self.active_user_client_connections: List[UserClientWebSocket] = []
self.collect_user_IDs = []
async def connect_the_user_client(self, websocket: WebSocket, THE_USER_ID):
await websocket.accept()
await self.send_message_to_absolutely_everybody(f"User: {THE_USER_ID} connected to server!")
print("User: {} ".format(THE_USER_ID) + " Connected")
if THE_USER_ID not in self.collect_user_IDs:
self.collect_user_IDs.append(THE_USER_ID)
else:
await self.send_message_to_absolutely_everybody(f"Somebody connected with the same ID as client: {THE_USER_ID}")
await self.send_message_to_absolutely_everybody("but Vlori is a nice and kind guy, so he wil not get kicked :)")
self.collect_user_IDs.append(THE_USER_ID)
self.active_user_client_connections.append(UserClientWebSocket(ws=websocket, id=THE_USER_ID))
await self.show_number_of_clients()
async def function_one_send_message_to_absolutely_everybody(self, message: str):
try:
await asyncio.sleep(2)
await asyncio.gather(*(conn.ws.send_text(message) for conn in self.active_webpage_client_connections))
await asyncio.gather(*(conn.ws.send_text(message) for conn in self.active_user_client_connections))
except:
print("waiting")
async def function_two_send_personal_message_to_user(self, message: str, websocket: WebSocket):
try:
await websocket.send_text(message)
except:
print("waiting for task..")
...
...
...
...
...
and further down is the channel in which client connects:
#app.websocket("/ws/testchannel")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
print_result = print("Received data: {} ".format(data))
send_data_back_to_user = await websocket.send_text(f"you sent message: {data}")
except WebSocketDisconnect as e:
print("client left chat, error = ", e)
The code as it stands now works perfectly, and the performance is good! However, if i add an async def function under the "send_data_back_to_user" line such as this:
await connection_manager.function_one_send_message_to_absolutely_everybody(data)
Then there is a huge latency! why is that?
I am playing around and tried this:
#app.websocket("/ws/testchannel")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
print_result = print("Received data: {} ".format(data))
send_data_back_to_user = await websocket.send_text(f"you sent message: {data}") # if i comment this line and the line underneath, and the speed is extremely fast!
the_asyncio_loop = asyncio.get_event_loop()
print_data_on_terminal = asyncio.gather(print_result)
return_data_back_to_user = asyncio.gather(send_data_back_to_user)
broadcast_msg = asyncio.gather(connection_manager.function_one_send_message_to_absolutely_everybody(data))
run_all_loops_together = asyncio.gather(print_data_on_terminal, return_data_back_to_user, broadcast_msg)
results = the_asyncio_loop.run_until_complete(run_all_loops_together)
print(results)
except WebSocketDisconnect as e:
print("client left chat, error = ", e)
but gives me the error:
TypeError: An asyncio.Future, a coroutine or an awaitable is required
could someone help me with this?
thanks.
I got this function:
async def download(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if resp.status == 200:
return resp
To get the data, I'd need to write:
text = await (await utils.download(url)).text()
How to change download so I can write like this
text = await utils.download(url).text()
without getting AttributeError: 'coroutine' object has no attribute 'text'?
May be this can help simplifying the usage:
async def download(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if resp.status == 200:
return await resp.text()
Then to use it:
async def main():
text = await download("http://python.org")
I'm using AioHttp to implement a service at work, and during my tests, I'm mocking a method, the issue is there the call for the method is calling the real instead of the mocked method.
#unittest_run_loop
#patch('export_api.main.add_job_to_db')
async def test_view_job(self, mocked_method):
json = {
"edl": "somedata"
}
response = await self.client.request("PUT", "/v1/job", json=json)
mocked_method.assert_called_once_with()
assert response.status == 200
So I get this error on the assertion of the mock:
msg = "Expected 'add_job_to_db' to be called once. Called 0 times."
My method on the main.py:
async def __call__(self, request):
"""Faz post do Job na fila do Render"""
data = await request.json()
job_id = uuid.uuid4()
job = Jobs(
job_id=str(job_id),
body=data
)
try:
add_job_to_db(self.app['db'], job)
return web.Response(status=200)
except DatabaseError as e:
print(e)
return web.Response(status=500)
Yes, is a callable method inside a class. The test work fine without the mocking. But I need to mock the call for the db, and I'm not having luck so far.
Any ideas?
Checklist:
Use asynctest package
Don't forget to use #asyncio.coroutine decorator around the unit test
Patch the object where it is used, in this case it export_api.main.add_job_to_db
I'm the author of mocket and few days ago I released the version 2.0.0 which fully supports asyncio/aiohttp.
Here is the same example of code mocking a URL on HTTP and on HTTPS:
import aiohttp
import asyncio
import async_timeout
from unittest import TestCase
from mocket.mocket import mocketize
from mocket.mockhttp import Entry
class AioHttpEntryTestCase(TestCase):
#mocketize
def test_http_session(self):
url = 'http://httpbin.org/ip'
body = "asd" * 100
Entry.single_register(Entry.GET, url, body=body, status=404)
Entry.single_register(Entry.POST, url, body=body*2, status=201)
async def main(l):
async with aiohttp.ClientSession(loop=l) as session:
with async_timeout.timeout(3):
async with session.get(url) as get_response:
assert get_response.status == 404
assert await get_response.text() == body
with async_timeout.timeout(3):
async with session.post(url, data=body * 6) as post_response:
assert post_response.status == 201
assert await post_response.text() == body * 2
loop = asyncio.get_event_loop()
loop.set_debug(True)
loop.run_until_complete(main(loop))
#mocketize
def test_https_session(self):
url = 'https://httpbin.org/ip'
body = "asd" * 100
Entry.single_register(Entry.GET, url, body=body, status=404)
Entry.single_register(Entry.POST, url, body=body*2, status=201)
async def main(l):
async with aiohttp.ClientSession(loop=l) as session:
with async_timeout.timeout(3):
async with session.get(url) as get_response:
assert get_response.status == 404
assert await get_response.text() == body
with async_timeout.timeout(3):
async with session.post(url, data=body * 6) as post_response:
assert post_response.status == 201
assert await post_response.text() == body * 2
loop = asyncio.get_event_loop()
loop.set_debug(True)
loop.run_until_complete(main(loop))
Source: https://github.com/mindflayer/python-mocket/blob/master/tests/tests35/test_http_aiohttp.py