WebSocket connection is not return any data? - python

I run this code the handshake and connection work fine but I don't get any results after I run the client.even though the code is working perfectly without any issues.
is there any idea to fix the problem?
note: I'm using graphql and Django framework in my project
import asyncio
import graphene
from channels.routing import ProtocolTypeRouter, URLRouter
class Query(graphene.ObjectType):
hello = graphene.String()
#staticmethod
def resolve_hello(obj, info, **kwargs):
return "world"
class Subscription(graphene.ObjectType):
"""Channels WebSocket consumer which provides GraphQL API."""
count_seconds = graphene.Float()
async def resolve_count_seconds(root, info):
for i in range(10):
yield i
await asyncio.sleep(1.)
schema = graphene.Schema(query=Query, subscription=Subscription)
class MyGraphqlWsConsumer(channels_graphql_ws.GraphqlWsConsumer):
"""Channels WebSocket consumer which provides GraphQL API."""
schema = schema
async def on_connect(self, payload):
pass
application = channels.routing.ProtocolTypeRouter({
"websocket": channels.routing.URLRouter([
django.urls.path("graphql/", MyGraphqlWsConsumer.as_asgi()),
])
})
ASGI_APPLICATION = 'graphql_ws.urls.application'
client:
from graphql_client import GraphQLClient
ws = GraphQLClient('ws://localhost:8000/graphql/')
def callback(_id, data):
print("got new data..")
print(f"msg id: {_id}. data: {data}")
query = """
subscription {
countSeconds
}
"""
sub_id = ws.subscribe(query, callback=callback)

Related

Cache or Reuse Mongodb connection in AWS lambda using Python

I'm building a serverless application using Python and Mongodb. In documentation I found that I need to write db connection outside handler function. I have used Mangum python package as adapter to handle API gateway.
from fastapi import FastAPI, Body, status, Depends
from mangum import Mangum
from motor.motor_asyncio import AsyncIOMotorClient
from fastapi.responses import JSONResponse
from app.utility.config import MONGODB_URL, MAX_CONNECTIONS_COUNT, MIN_CONNECTIONS_COUNT, MAX_DB_THREADS_WAIT_COUNT, MAX_DB_THREAD_QUEUE_TIMEOUT_COUNT
application= FastAPI()
client = AsyncIOMotorClient(str(MONGODB_URL),
maxPoolSize=MAX_CONNECTIONS_COUNT,
minPoolSize=MIN_CONNECTIONS_COUNT,
waitQueueMultiple = MAX_DB_THREADS_WAIT_COUNT,
waitQueueTimeoutMS = MAX_DB_THREAD_QUEUE_TIMEOUT_COUNT )
async def get_database() -> AsyncIOMotorClient:
return client
#application.post("/createStudent")
async def create_student(student = Body(...), db: AsyncIOMotorClient = Depends(get_database)):
new_student = await db["college"]["students"].insert_one(student)
created_student = await db["college"]["students"].find_one({"_id": new_student.inserted_id})
return JSONResponse(status_code=status.HTTP_201_CREATED, content=created_student)
#application.post("/createTeacher")
async def create_teacher(teacher = Body(...), db: AsyncIOMotorClient = Depends(get_database)):
new_teacher = await db["college"]["students"].insert_one(teacher)
created_teacher = await db["college"]["students"].find_one({"_id": new_teacher.inserted_id})
return JSONResponse(status_code=status.HTTP_201_CREATED, content=created_teacher)
handler = Mangum(application)
For every API request, new connection is created. How to cache db so that new request uses old connection. Every time new request is created so that lambda compute time is increased dramatically after db hits max connection limit.
There are examples for node js but for python I could not find anywhere

Is it possible to have an api call another api, having them both in same application?

I have a python application running on my localhost:3978. Is it possible to make an api call to http://localhost:3978/api/users from http://localhost:3978/api/accounts?
#routes.get("/api/accounts")
async def accounts(request):
api_url = "http://127.0.0.1:3978/api/users"
response = requests.get(api_url)
return json_response(data=respone.json())
#routes.get("/api/users")
async def users(request):
pass
You can use url_path_for() to feed in RedirectResponse of starlette.responses as stated here. For example:
from fastapi import FastAPI
from starlette.responses import RedirectResponse
app = FastAPI()
#app.get("/a")
async def a():
return {"message": "Hello World"}
#app.get('/b')
async def b():
url = app.url_path_for("a")
response = RedirectResponse(url=url)
return response

Testing subscriptions using graphene.test in Python Graphene

What is the idiomatic approach for testing subscriptions in graphene-python? It seems that the client.execute option in graphene.test is only appropriate for Query testing.
P.S. There is a subscription execution example in the documentation but it does not seem to be part of the testing library (https://docs.graphene-python.org/en/latest/execution/subscriptions/).
The pre-release version of graphene (3) supports subscriptions in this way:
import asyncio
from datetime import datetime
from graphene import ObjectType, String, Schema, Field
class Query(ObjectType):
hello = String()
def resolve_hello(root, info):
return 'Hello, world!'
class Subscription(ObjectType):
time_of_day = Field(String)
async def resolve_time_of_day(root, info):
while True:
yield datetime.now().isoformat()
await asyncio.sleep(1)
schema = Schema(query=Query, subscription=Subscription)
async def main():
subscription = 'subscription { timeOfDay }'
result = await schema.execute_async(subscription)
async for item in result:
print(item)
asyncio.run(main())
Source: https://github.com/graphql-python/graphene/issues/1099.
This is how you do it in 2022 with Graphene 3:
pip install pytest pytest-asyncio
import pytest
from main.schema import schema
#pytest.mark.asyncio
async def test_realtime_updates():
q = 'subscription { count }'
result = await schema.subscribe(q)
async for item in result:
print(item)
pytest.fail(pytrace=False)
If you don't have any app, here's a minimal example with subscriptions:
pip install graphene graphene_starlette3
# Graphene Subscriptions demo
import graphene
from graphene import ResolveInfo
from starlette_graphene3 import GraphQLApp, make_playground_handler
class Query(graphene.ObjectType):
hello = graphene.String()
class Subscription(graphene.ObjectType):
count = graphene.Float()
async def subscribe_count(_, info: ResolveInfo):
for i in range(10):
yield i
# GraphQL Schema
schema = graphene.Schema(
query=Query,
subscription=Subscription,
)
# ASGI application
# You'll probably want to mount it to a Starlette application
app = GraphQLApp(
schema=schema,
on_get=make_playground_handler(),
)
Run it:
$ uvicorn run main:app --reload

How to run discord.py tasks within celery?

I tried to create a celery task that returns the roles from my discord guild.: (proof of concept)
...
from asgiref.sync import async_to_sync
from celery import shared_task, Task
from discord.http import HTTPClient
class ChatTask(Task):
_client = None
#property
def client(self) -> HTTPClient:
if self._client is None:
client = HTTPClient()
result = async_to_sync(
lambda: client.static_login(settings.BOT_TOKEN, bot=True)
)()
self._client = client
return self._client
#shared_task(base=ChatTask, bind=True)
def get_roles(self):
# Still testing!
client: HTTPClient = self.client
print(client) # Seems to be fine: `<discord.http.HTTPClient object at 0x7fcc6728dbb0>`
roles = async_to_sync(lambda: async_get_roles(client))() # ERROR!: `RuntimeError('Event loop is closed')`
print(roles)
return roles
However I keep getting RuntimeError('Event loop is closed').
Comment: I do know that celery v5 will support async/await. But I do not want to wait until the release at the end of the year. ;)
How can I fix this code to get rid of the error and actually get see the roles printed out to the console?

How to Upgrade HTTP Request to Websocket (Autobahn & Twisted Web )

To give you an idea of what I am trying to accomplish with Twisted Web and Autobahn websockets: my UI currently sends an initial HTTP GET request with an upgrade to a websocket in the header. Upon reading that in Twisted Web, the connection needs to switch from HTTP to a websocket protocol to pass data back and forth. Note that this websocket upgrade happens on the same port, port 8000.
Does anyone know how I can implement what I am trying to do? Thank you so much.
EDIT: updated code for working example. You can find it here: Payload from POST Request is Cutoff (Twisted Web)
Here is my code using Twisted Web:
class HttpResource(resource.Resource):
isLeaf = 1
def __init__(self):
self.children = {}
self.ws_port = None
print 'resource invoked'
def render_GET(self, request):
print 'render invoked'
if request.getHeader('Sec-WebSocket-Key'):
# Processing the Key as per RFC 6455
key = request.getHeader('Sec-WebSocket-Key')
h = hashlib.sha1(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
request.setHeader('Sec-WebSocket-Accept', base64.b64encode(h.digest()))
# setting response headers
request.setHeader('Upgrade', 'websocket')
request.setHeader('Connection', 'Upgrade')
request.setResponseCode(101)
return ''
else:
log("Regular HTTP GET request.")
return "<html><body style='margin: 0; overflow: hidden;'><iframe style='width: 100%; height: 100%; border: none;' src='http://tsa-graphiql.herokuapp.com/'></iframe></body></html>"
def render_POST(self,request):
log("POST request")
request.setResponseCode(200)
def handle_single_query(self, queryData):
log("Handle single query data.")
return
class HttpWsChannel(http.HTTPChannel):
def dataReceived(self, data):
log('Data received:\n{}'.format(data))
if data.startswith('GET'):
# This will invoke the render method of resource provided
http.HTTPChannel.dataReceived(self, data)
if data.startswith('POST'):
http.HTTPChannel.dataReceived(self, data)
else:
"""
Pass binary data to websocket class.
"""
ws_protocol = self.site.ws_factory.protocol(self.site.ws_factory.connection_subscriptions)
log(ws_protocol)
#just echo for now
# self.transport.write(data)
class HttpFactory(Site):
"""
Factory which takes care of tracking which protocol
instances or request instances are responsible for which
named response channels, so incoming messages can be
routed appropriately.
"""
def __init__(self, resource):
http.HTTPFactory.__init__(self)
self.resource = resource
self.ws_factory = WsProtocolFactory("ws://127.0.0.1:8000")
self.ws_factory.protocol = WsProtocol
def buildProtocol(self, addr):
try:
channel = HttpWsChannel()
channel.requestFactory = self.requestFactory
channel.site = self
return channel
except Exception as e:
log("Could not build protocol: {}".format(e))
site = HttpFactory(HttpResource())
if __name__ == '__main__':
reactor.listenTCP(8000, site)
reactor.run()
EDIT 7/8/2017: Here is the new code I am trying below. The websocket messages are received successfully via the onMessage method. However the HTTP requests are not working. The current error I am getting on a GET request is:
<html>
<head><title>404 - No Such Resource</title></head>
<body>
<h1>No Such Resource</h1>
<p>No such child resource.</p>
</body>
</html>
Python code
from twisted.web.server import (
Site,
)
from twisted.internet import reactor
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class WebSocketProtocol(WebSocketServerProtocol):
def onConnect(self, request):
print("WebSocket connection request: {}".format(request))
def onMessage(self, payload, isBinary):
print("onMessage: {}".format(payload))
if __name__ == '__main__':
factory = WebSocketServerFactory()
factory.protocol = WebSocketProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild(b"ws", resource)
site = Site(root)
reactor.listenTCP(8000, site)
reactor.run()
Use WebSocketResource to expose a WebSocketServerFactory as part of a Site.
from twisted.web.server import (
Site,
)
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class YourAppProtocol(WebSocketServerProtocol):
def onConnect(self, request):
...
...
def main():
factory = WebSocketRendezvousFactory()
factory.protocol = YourAppProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild(b"some-path-segment", resource)
root.putChild(...)
site = Site(root)
reactor.listenTCP(8080, site)
reactor.run()
The problems with truncated request bodies is probably because your upgrade protocol implementation is buggy. There is no framing applied at the level of dataReceived so you can't expect checks like startswith("GET") to be reliable.
Using WebSocketResource and Site gives you the correct HTTP parsing code from Twisted Web and Autobahn while also allowing you to speak WebSocket to a particular URL and regular HTTP to others.
So after reading a little bit on Google, I found this website that explains how to upgrade the HTTP connection to a websocket connection via Autobahn Twisted: Read and Set request headers via Autobahn Twisted.
The code that I was able to get to work is shown below!
from twisted.web.server import (
Site,
)
from twisted.internet import reactor
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class HttpResource(Resource):
isLeaf = True
def render_GET(self, request):
return "<html><body style='margin: 0; overflow: hidden;'><iframe style='width: 100%; height: 100%; border: none;' src='http://tsa-graphiql.herokuapp.com/'></iframe></body></html>"
class WebSocketProtocol(WebSocketServerProtocol):
def onConnect(self, request):
custom_header = {}
if request.headers['sec-websocket-key']:
custom_header['sec-websocket-protocol'] = 'graphql-ws'
return (None, custom_header)
def onMessage(self, payload, isBinary):
print("onMessage: {}".format(payload))
if __name__ == '__main__':
factory = WebSocketServerFactory()
factory.protocol = WebSocketProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild("", HttpResource())
root.putChild(b"ws", ws_resource)
site = Site(root)
reactor.listenTCP(8000, site)

Categories

Resources