Hwo can i emit Flask-SocketIO messages outside of socketio context? - flask-socketio

A emitted message from my python flask webserver (flask socketio server) does not arrive at the javascript socketio client.
Within a socketio context everything works fine.
I already read all the topics here about that problem... so i included the monkey.patch, tried eventlet as async_mode and added some other hints, so i got the following codebase now:
app = Flask(__name__)
app.secret_key = "zISMDCwvfJY2TXHMKHrB"
sio = SocketIO(app, async_mode='eventlet')
eventlet.monkey_patch()
#app.route('/index')
def index():
someotherfunction()
return render_template('index.html')
#sio.event
def my_event(message):
print(message)
#sio.event
def hello(message):
sio.emit('hello', {'data': 'got it!'})
print(message)
def someotherfunction():
with app.app_context():
sio.emit('hello', {'data': 'got it123123!'}, namespace='/')
if __name__ == "__main__":
sio.run(app, host="localhost", port="8080", debug=True)
and here the socketio client side segment:
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('connect', function()
{
socket.emit('my_event', {data: 'I\'m connected!'});
});
socket.on('hello', function(data)
{
console.log(data);
});
</script>
So the 'hello' function will emit the data successfully after request, but someotherfunction() will not emit any data to the client side.

Related

Oak framework Doesnt respond to request in deno deploy

I tried deploying my deno app to deno deploy but I have tried all means to work but still no response and I have no error in logs.
This my code below..
import { load } from "https://deno.land/std#0.171.0/dotenv/mod.ts";
import { Application } from "https://deno.land/x/oak#v11.1.0/mod.ts";
import { socketIo } from "../src/controllers/websocket/setup.ts";
import fileRouter from "./../src/routes/file_rt.ts";
import ordersRouter from "./../src/routes/orders_rt.ts";
import mealRouter from "./../src/routes/meal_rt.ts";
import userRouter from "./../src/routes/user_rt.ts";
load();
const app = new Application();
app.use(await rateLimit);
app.use(userRouter.routes());
app.use(ordersRouter.routes());
app.use(mealRouter.routes());
app.use(fileRouter.routes());
app.use(userRouter.allowedMethods());
app.use(ordersRouter.allowedMethods());
app.use(mealRouter.allowedMethods());
app.use(fileRouter.allowedMethods());
socketIo();
await app.listen({port:80});
I tried to test an api route using postman but the endpoint didn't log anything
I have fixed it by removing the socket IO connection I imported. [ socketIO() ]
But now the socket connection is not working.
export const socketIo = async () => {
io.on("connection", (socket) => {
console.log(`socket ${socket.id} connected`);
skt = socket;
signUSER(socket);
socket.on("disconnect", (reason) => {
console.log(`socket ${socket.id} disconnected due to ${reason}`);
});
console.log("Socket Hit 😎✨");
});
await serve(io.handler(), {
port: 3000,
});
}

Handling TimeOut Exception in AsyncIO

import asyncio
import aiohttp
from time import perf_counter
import csv
path = "*******************"
domains = []
total_count=0
with open(path, 'r') as file:
csvreader = csv.reader(file)
for row in csvreader:
try:
website = row[4].split("//")[-1].split("www.")[-1].split('/')[0]
if website == "":
continue
domains.append(website)
except:
continue
sample = domains[0:50]
async def fetch(s, body):
async with s.post('https://****************', json=body) as r:
if r.status!= 200:
pass
enrich_response = await r.json()
#print(enrich_response)
employees = enrich_response['employees']
for employee in employees:
if(employee['job_title'] == "Owner"):
print(employee)
print("************************************************")
global total_count
total_count += 1
print("Total Count:", total_count)
continue
elif(employee['job_title'] == "CEO"):
print(employee)
print("***************************************************")
total_count+=1
print("Total Count:", total_count)
continue
else:
continue
async def fetch_all(s,bodies):
tasks = []
for body in bodies:
task = asyncio.create_task(fetch(s, body))
tasks.append(task)
res = await asyncio.gather(*tasks)
return res
async def main():
# apikeys = list(apikeysone.keys.values())
bodies = []
for domain in sample:
body = {
"api_key": "********************************",
"domain" : "{}".format(domain)
}
bodies.append(body)
async with aiohttp.ClientSession() as session:
data = await fetch_all(session, bodies)
print(data[0])
if __name__ == '__main__':
start = perf_counter()
try:
asyncio.run(main())
except Exception as e:
print(e)
pass
stop = perf_counter()
print("Time taken:", stop - start)
Hi!
I'm trying to connect to a scraping service provider using asyncio, instead of simple synchronous api calls.
But I get a TimeOut error. How could I use exception handling to wait a few seconds before retrying it once again? Or just skipping that task if it fails?
Thank you in advance fellow coder!
Tried adding to some places continue/pass
Try exploring asyncio.wait_for() function. It takes an awaitable and a timeout value. If task isn't completed before timeout value, it raises asyncio.exceptions.TimeoutError which you can handle in any way you want in except clause.
A typical example (from Python doc) is as follows:
async def eternity():
# Sleep for one hour
await asyncio.sleep(3600)
print('yay!')
async def main():
# Wait for at most 1 second
try:
await asyncio.wait_for(eternity(), timeout=1.0)
except TimeoutError:
print('timeout!')
asyncio.run(main())
# Expected output:
#
# timeout!

pytest with httpx.AsyncClient cannot find newly created database records

I am trying to setup pytest with httpx.AsyncClient and sqlalchemy AsyncSession with FastAPI. Everything practically mimics the tests in FastAPI Fullstack repo, except for async stuff.
No issues with CRUD unit tests. The issue arises when running API tests using AsyncClient from httpx lib.
The issue is, any request made by client only has access to the users (in my case) created before initializing (setting up) the client fixture.
My pytest conftest.py setup is like this:
from typing import Dict, Generator, Callable
import asyncio
from fastapi import FastAPI
import pytest
# from sqlalchemy.orm import Session
from sqlalchemy.ext.asyncio import AsyncSession
from httpx import AsyncClient
import os
import warnings
import sqlalchemy as sa
from alembic.config import Config
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import sessionmaker
async def get_test_session() -> Generator:
test_engine = create_async_engine(
settings.SQLALCHEMY_DATABASE_URI + '_test',
echo=False,
)
# expire_on_commit=False will prevent attributes from being expired
# after commit.
async_sess = sessionmaker(
test_engine, expire_on_commit=False, class_=AsyncSession
)
async with async_sess() as sess, sess.begin():
yield sess
#pytest.fixture(scope="session")
async def async_session() -> Generator:
test_engine = create_async_engine(
settings.SQLALCHEMY_DATABASE_URI + '_test',
echo=False,
pool_size=20, max_overflow=0
)
# expire_on_commit=False will prevent attributes from being expired
# after commit.
async_sess = sessionmaker(
test_engine, expire_on_commit=False, class_=AsyncSession
)
yield async_sess
#pytest.fixture(scope="session")
async def insert_initial_data(async_session:Callable):
async with async_session() as session, session.begin():
# insert first superuser - basic CRUD ops to insert data in test db
await insert_first_superuser(session)
# insert test.superuser#example.com
await insert_first_test_user(session)
# inserts test.user#example.com
#pytest.fixture(scope='session')
def app(insert_initial_data) -> FastAPI:
return FastAPI()
#pytest.fixture(scope='session')
async def client(app: FastAPI) -> Generator:
from app.api.deps import get_session
app.dependency_overrides[get_session] = get_test_session
async with AsyncClient(
app=app, base_url="http://test",
) as ac:
yield ac
# reset dependencies
app.dependency_overrides = {}
So in this case, only the superuser test.superuser#example.com and normal user test.user#example.com are available during running API tests. e.g., code below is able to fetch the access token just fine:
async def authentication_token_from_email(
client: AsyncClient, session: AsyncSession,
) -> Dict[str, str]:
"""
Return a valid token for the user with given email.
"""
email = 'test.user#example.com'
password = 'test.user.password'
user = await crud.user.get_by_email(session, email=email)
assert user is not None
data = {"username": email, "password": password}
response = await client.post(f"{settings.API_V1_STR}/auth/access-token",
data=data)
auth_token = response.cookies.get('access_token')
assert auth_token is not None
return auth_token
but, the modified code below doesn't - here I try to insert new user, and then log in to get access token.
async def authentication_token_from_email(
client: AsyncClient, session: AsyncSession,
) -> Dict[str, str]:
"""
Return a valid token for the user with given email.
If the user doesn't exist it is created first.
"""
email = random_email()
password = random_lower_string()
user = await crud.user.get_by_email(session, email=email)
if not user:
user_in_create = UserCreate(email=email,
password=password)
user = await crud.user.create(session, obj_in=user_in_create)
else:
user_in_update = UserUpdate(password=password)
user = await crud.user.update(session, db_obj=user, obj_in=user_in_update)
assert user is not None
# works fine up to this point, user inserted successfully
# now try to send http request to fetch token, and user is not found in the db
data = {"username": email, "password": password}
response = await client.post(f"{settings.API_V1_STR}/auth/access-token",
data=data)
auth_token = response.cookies.get('access_token')
# returns None.
return auth_token
What is going on here ? Appreciate any help!
Turns out all I needed to do is, for reason I do not understand, is to define the FastAPI dependency override function inside the client fixture:
before
async def get_test_session() -> Generator:
test_engine = create_async_engine(
settings.SQLALCHEMY_DATABASE_URI + '_test',
echo=False,
)
# expire_on_commit=False will prevent attributes from being expired
# after commit.
async_sess = sessionmaker(
test_engine, expire_on_commit=False, class_=AsyncSession
)
async with async_sess() as sess, sess.begin():
yield sess
#pytest.fixture(scope='session')
async def client(app: FastAPI) -> Generator:
from app.api.deps import get_session
app.dependency_overrides[get_session] = get_test_session
async with AsyncClient(
app=app, base_url="http://test",
) as ac:
yield ac
# reset dependencies
app.dependency_overrides = {}
after
#pytest.fixture(scope="function")
async def session(async_session) -> Generator:
async with async_session() as sess, sess.begin():
yield sess
#pytest.fixture
async def client(app: FastAPI, session:AsyncSession) -> Generator:
from app.api.deps import get_session
# this needs to be defined inside this fixture
# this is generate that yields session retrieved from `session` fixture
def get_sess():
yield session
app.dependency_overrides[get_session] = get_sess
async with AsyncClient(
app=app, base_url="http://test",
) as ac:
yield ac
app.dependency_overrides = {}
I'd appreciate any explanation of this behavior. Thanks!

FastAPI as backend for Telethon

I'm trying to make api auth with telethon work. I'm sending request to endpoint where telegram client is initialized and trying to send code request to telegram. But there is input() and I didn't find any solution to pass code as variable
#router.get('/code')
async def send_code_request(phone: str):
client = get_telegram_client(phone)
await client.start(phone)
return {'msg': 'code sent'}
I found easier solution, but there is one con - when authorizing via session sign_in() method is requiring to execute send_code_request() method first so there is will be 2 same code messages
async def get_telegram_client(session: str = None) -> TelegramClient:
return TelegramClient(
StringSession(session),
api_id=settings.TELEGRAM_API_ID,
api_hash=settings.TELEGRAM_API_HASH
)
#router.post('/code')
async def send_authorizarion_code(payload: TelegramSendCode):
client = await get_telegram_client()
await client.connect()
try:
await client.send_code_request(payload.phone)
except FloodWaitError as e:
return {
'FloodWaitError': {
'phone_number': e.request.phone_number,
'seconds': e.seconds
}}
else:
return {
'msg': 'code sent',
'session': client.session.save()
}
#router.post('/auth')
async def authorize(payload: TelegramAuth):
client = await get_telegram_client(payload.session)
await client.connect()
await client.send_code_request(payload.phone)
await client.sign_in(code=payload.code, phone=payload.phone)
return {'msg': 'signed in'}
I'm assuming you're using .start() for that.
.start() accepts a callback that is by default input() you can pass your own input like so.
client.start(code_callback=your_callback) and your callback should should return the code.
This can all be found here in the start docs

python3.5: with aiohttp is it possible to serve several responses concurently?

I'm using the latest version (1.0.2) of aiohttp with python3.5 I have the following server code
import asyncio
from aiohttp.web import Application, Response, StreamResponse, run_app
async def long(request):
resp = StreamResponse()
name = request.match_info.get('name', 'Anonymous')
resp.content_type = 'text/plain'
for _ in range(1000000):
answer = ('Hello world\n').encode('utf8')
await resp.prepare(request)
resp.write(answer)
await resp.write_eof()
return resp
async def init(loop):
app = Application(loop=loop)
app.router.add_get('/long', long)
return app
loop = asyncio.get_event_loop()
app = loop.run_until_complete(init(loop))
run_app(app)
If I then run two curl requests curl http://localhost:8080/long in different terminals, only the first one will receive data
My thought was that using asyncio you could, in a monothreaded code, start serving other response, while an other is waiting for I/O
Most of the code I found online about concurent+asyncio only talks about the client side, but not server side
Am I missing something or is my comprehension of how asyncio works is flawed ?
Just push await resp.drain() after resp.write() for giving aiohttp a chance to switch between tasks:
import asyncio
from aiohttp.web import Application, Response, StreamResponse, run_app
async def long(request):
resp = StreamResponse()
name = request.match_info.get('name', 'Anonymous')
resp.content_type = 'text/plain'
await resp.prepare(request) # prepare should be called once
for _ in range(1000000):
answer = ('Hello world\n').encode('utf8')
resp.write(answer)
await resp.drain() # switch point
await resp.write_eof()
return resp
async def init(loop):
app = Application(loop=loop)
app.router.add_get('/long', long)
return app
loop = asyncio.get_event_loop()
app = loop.run_until_complete(init(loop))
run_app(app)

Resources