How to test python tornado application that use Mongodb with Motor Client - python

I want to test my tornado python application with pytest.
for that purpose, I want to have a mock db for the mongo and to use motor "fake" client to simulate the calls to the mongodb.
I found alot of solution for pymongo but not for motor.
any idea?

I do not clearly understand your problem — why not just have hard-coded JSON data?
If you just want to have a class that would mock the following:
from motor.motor_tornado import MotorClient
client = MotorClient(MONGODB_URL)
my_db = client.my_db
result = await my_db['my_collection'].insert_one(my_json_load)
So I recommend creating a Class:
Class Collection():
database = []
async def insert_one(self,data):
database.append(data)
data['_id'] = "5063114bd386d8fadbd6b004" ## You may make it random or consequent
...
## Also, you may save the 'database' list to the pickle on disk to preserve data between runs
return data
async def find_one(self, data):
## Search in the list
return data
async def delete_one(self, data_id):
delete_one.deleted_count = 1
return
## Then create a collection:
my_db = {}
my_db['my_collecton'] = Collection()
### The following is the part of 'views.py'
from tornado.web import RequestHandler, authenticated
from tornado.escape import xhtml_escape
class UserHandler(RequestHandler):
async def post(self, name):
getusername = xhtml_escape(self.get_argument("user_variable"))
my_json_load = {'username':getusername}
result = await my_db['my_collection'].insert_one(my_json_load)
...
return self.write(result)
If you would clarify your question, I will develop my answer.

Related

What is the recommended way to instantiate and pass around a redis client with FastAPI

I'm using FastAPI with Redis. My app looks something like this
from fastapi import FastAPI
import redis
# Instantiate redis client
r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
# Instantiate fastapi app
app = FastAPI()
#app.get("/foo/")
async def foo():
x = r.get("foo")
return {"message": x}
#app.get("/bar/")
async def bar():
x = r.get("bar")
return {"message": x}
Is it bad practice to create r as a module-scoped variable like this? If so what are the drawbacks?
In Tiangolo's tutorial on setting up a SQL database connection he uses a dependency, which I guess in my case would look something like this
from fastapi import Depends, FastAPI
import redis
# Instantiate fastapi app
app = FastAPI()
# Dependency
def get_redis():
return redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
#app.get("/foo/")
async def foo(r = Depends(get_redis)):
x = r.get("foo")
return {"message": x}
#app.get("/bar/")
async def bar(r = Depends(get_redis)):
x = r.get("bar")
return {"message": x}
I'm a bit confused as to which of these methods (or something else) would be preferred and why.
Depends will evaluate every time your function got a request, so your second example will create a new connection for each request. As #JarroVGIT said, we can use connection pooling to maintain the connection from FastAPI to Redis and reduce open-closing connection costs.
Usually, I create a different file to define the connection. Let's say we have config/db.py:
import redis
def create_redis():
return redis.ConnectionPool(
host='localhost',
port=6379,
db=0,
decode_responses=True
)
pool = create_redis()
Then in the main.py
from fastapi import Depends, FastAPI
import redis
from config.db import pool
app = FastAPI()
def get_redis():
# Here, we re-use our connection pool
# not creating a new one
return redis.Redis(connection_pool=pool)
#app.get("/items/{item_id}")
def read_item(item_id: int, cache = Depends(get_redis)):
status = cache.get(item_id)
return {"item_name": status}
#app.put("/items/{item_id}")
def update_item(item_id: int, cache = Depends(get_redis)):
cache.set(item_id, "available")
return {"status": "available", "item_id": item_id}
Usually, I also split the dependencies file like the doc so we can call it from our routing module, but for simplicity, I will leave it like this.
You can check this repo to experiment by yourself. It has more comprehensive code and I have already created several scenarios that might help you understand the difference. And it will also cover how your first example may block other endpoints.
In your second example, every time your creating new redis instance and one time it reach max connection limit. If you put code like this that much more clean and re-usable,
from fastapi import FastAPI
import redis
class AppAPI(FastAPI):
def __init__(self):
self.redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
#app.get("/foo/")
async def foo():
x = self.redis_client.get("foo")
return {"message": x}
https://github.com/redis/redis-py#connection-pools. You can define the pool at module level and import it wherever needed. All Redis connection will be created out of the pool.
pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
r = redis.Redis(connection_pool=pool)

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

handling async streaming request in grpc python

I am trying to understand how to handle a grpc api with bidirectional streaming (using the Python API).
Say I have the following simple server definition:
syntax = "proto3";
package simple;
service TestService {
rpc Translate(stream Msg) returns (stream Msg){}
}
message Msg
{
string msg = 1;
}
Say that the messages that will be sent from the client come asynchronously ( as a consequence of user selecting some ui elements).
The generated python stub for the client will contain a method Translate that will accept a generator function and will return an iterator.
What is not clear to me is how would I write the generator function that will return messages as they are created by the user. Sleeping on the thread while waiting for messages doesn't sound like the best solution.
This is a bit clunky right now, but you can accomplish your use case as follows:
#!/usr/bin/env python
from __future__ import print_function
import time
import random
import collections
import threading
from concurrent import futures
from concurrent.futures import ThreadPoolExecutor
import grpc
from translate_pb2 import Msg
from translate_pb2_grpc import TestServiceStub
from translate_pb2_grpc import TestServiceServicer
from translate_pb2_grpc import add_TestServiceServicer_to_server
def translate_next(msg):
return ''.join(reversed(msg))
class Translator(TestServiceServicer):
def Translate(self, request_iterator, context):
for req in request_iterator:
print("Translating message: {}".format(req.msg))
yield Msg(msg=translate_next(req.msg))
class TranslatorClient(object):
def __init__(self):
self._stop_event = threading.Event()
self._request_condition = threading.Condition()
self._response_condition = threading.Condition()
self._requests = collections.deque()
self._last_request = None
self._expected_responses = collections.deque()
self._responses = {}
def _next(self):
with self._request_condition:
while not self._requests and not self._stop_event.is_set():
self._request_condition.wait()
if len(self._requests) > 0:
return self._requests.popleft()
else:
raise StopIteration()
def next(self):
return self._next()
def __next__(self):
return self._next()
def add_response(self, response):
with self._response_condition:
request = self._expected_responses.popleft()
self._responses[request] = response
self._response_condition.notify_all()
def add_request(self, request):
with self._request_condition:
self._requests.append(request)
with self._response_condition:
self._expected_responses.append(request.msg)
self._request_condition.notify()
def close(self):
self._stop_event.set()
with self._request_condition:
self._request_condition.notify()
def translate(self, to_translate):
self.add_request(to_translate)
with self._response_condition:
while True:
self._response_condition.wait()
if to_translate.msg in self._responses:
return self._responses[to_translate.msg]
def _run_client(address, translator_client):
with grpc.insecure_channel('localhost:50054') as channel:
stub = TestServiceStub(channel)
responses = stub.Translate(translator_client)
for resp in responses:
translator_client.add_response(resp)
def main():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
add_TestServiceServicer_to_server(Translator(), server)
server.add_insecure_port('[::]:50054')
server.start()
translator_client = TranslatorClient()
client_thread = threading.Thread(
target=_run_client, args=('localhost:50054', translator_client))
client_thread.start()
def _translate(to_translate):
return translator_client.translate(Msg(msg=to_translate)).msg
translator_pool = futures.ThreadPoolExecutor(max_workers=4)
to_translate = ("hello", "goodbye", "I", "don't", "know", "why",)
translations = translator_pool.map(_translate, to_translate)
print("Translations: {}".format(zip(to_translate, translations)))
translator_client.close()
client_thread.join()
server.stop(None)
if __name__ == "__main__":
main()
The basic idea is to have an object called TranslatorClient running on a separate thread, correlating requests and responses. It expects that responses will return in the order that requests were sent out. It also implements the iterator interface so that you can pass it directly to an invocation of the Translate method on your stub.
We spin up a thread running _run_client which pulls responses out of TranslatorClient and feeds them back in the other end with add_response.
The main function I included here is really just a strawman since I don't have the particulars of your UI code. I'm running _translate in a ThreadPoolExecutor to demonstrate that, even though translator_client.translate is synchronous, it yields, allowing you to have multiple in-flight requests at once.
We recognize that this is a lot of code to write for such a simple use case. Ultimately, the answer will be asyncio support. We have plans for this in the not-too-distant future. But for the moment, this sort of solution should keep you going whether you're running python 2 or python 3.

Calling a python class method from an instance of a class doesn't work as I expect

I'm new to Python, and I think I'm trying to do something simple. However, I am confused with the results I am getting. I am declaring a class that has 2 class methods, add and remove, which in my simple example add or remove a client from a list class variable. Here's my code:
Service.py
from Client import Client
class Service:
clients = []
#classmethod
def add(cls, client):
cls.clients.append(client)
#classmethod
def remove(cls, client):
if client in cls.clients:
cls.clients.remove(client)
if __name == '__main__'
a = Client()
b = Client()
c = Client()
Service.add(a)
Service.add(b)
Service.add(c)
print(Service.clients)
c.kill()
print(Service.clients)
Service.remove(c)
print(Service.clients)
Client.py
class Client:
def kill(self):
from Service import Service
Service.remove(self)
I would expect calling c.kill() would remove the instance from the clients list.
However, when I evaluate the clients list, it is showing 0 items. when I call Service.remove(c), it shows the correct list, and removes it as expected. I am not sure what I am missing here.
If it matters, I am currently using PyCharm with my code running in a Virtualenv with Python 3.6.5.
Your current code is using circular imports, as both files utilize each other. Also, instead of relying on the client to destroy the connections, use a contextmanager to facilitate the updating of clients, and at the end of the procedure, empty clients:
import contextlib
class Client:
pass
class Service:
clients = []
#classmethod
def add(cls, client):
cls.clients.append(client)
#classmethod
#contextlib.contextmanager
def thread(cls):
yield cls
cls.clients = []
with Service.thread() as t:
t.add(Client())
t.add(Client())

How to make sure that the object gets updated from module to the downstream?

I'm not sure if my title is misleading but I'm a python noob. I'm doing an interface for Vault. What it's doing is just renewing the keys every 30s so the client doesn't have to do it. Otherwise the secret will expire or change and the client using the key will get an error. I'm using python threading to renew the keys. Here's my code.
from __future__ import print_function
import hvac
import time
import threading
import os
VAULT_URL = os.environ['VAULT_ADDR']
VAULT_TOKEN = os.environ['VAULT_TOKEN']
class Client:
def __init__(self, *keys):
self.keys = keys
self.data_dict = {}
self.client = hvac.Client(
url=VAULT_URL,
token=VAULT_TOKEN)
self.__renew()
def read(self):
for key in self.keys:
self.data_dict[key] = self.client.read(key)
return self.data_dict
def __renew(self):
self.client.renew_token()
threading.Timer(30, self.__renew).start()
self.read()
And this is how it's being used.
from cnvault.cnvault import Client
data_dict = Client('secret/key').read()
// This is for web.py just to test
class ping:
def GET(self):
print(data_dict)
return 'pong'
Now if I change the data in Vault using Vault CLI and I call /ping, I'm still seeing old data.

Categories

Resources