Sanic: Issue while following sanic motor example

Created on 26 May 2017  路  12Comments  路  Source: sanic-org/sanic

Traceback

2017-05-26 14:22:13 - (sanic)[ERROR]: Exception occurred while handling uri: "http://127.0.0.1:1230/write"
Traceback (most recent call last):
  File "/home/sreenadh/Projects/Sanic_tests/hello/.hello/lib/python3.5/site-packages/sanic/app.py", line 471, in handle_request
    response = await response
  File "/usr/lib/python3.5/asyncio/coroutines.py", line 124, in throw
    return self.gen.throw(exc)
  File "app.py", line 18, in write_db
    obj_id = await client.hello.write_col.save(data)
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
RuntimeError: Task <Task pending coro=<Sanic.handle_request() running at /home/sreenadh/Projects/Sanic_tests/hello/.hello/lib/python3.5/site-packages/sanic/app.py:471> created at /home/sreenadh/Projects/Sanic_tests/hello/.hello/lib/python3.5/site-packages/sanic/server.py:185> got Future <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.5/asyncio/futures.py:431]> attached to a different loop

I have just started using Sanic, and I actually find it ver good, since am a big fan of Flask and simplicity that it gives. So I was following the same way as mentioned in the sanic_motor.py to test out writing some simple data from a POST request. i.e.:

def get_client():
    from motor.motor_asyncio import AsyncIOMotorClient
    client = AsyncIOMotorClient("mongodb://user:password@localhost:27045")
    return client
.
.
.

@app.route('/write',methods=['POST'])
async def write_db(request):
    data = request.json
    client = get_client()
    obj_id = await client.hello.write_col.save(data)
    return response.json({ 'status':True, 'id':str(obj_id) })

Am I doing something wrong here?
Can anyone comment on this! :thinking:

Most helpful comment

@andreheringer
I think I could do something similar to this:

models/__ init__.py

from motor.motor_asyncio import AsyncIOMotorClient

mongo_uri = "mongodb://127.0.0.1:27017/test"
db = AsyncIOMotorClient(mongo_uri)['database']

so you can do this anywhere

from models import db

All 12 comments

Motor client need to use same ioloop as sanic. You can initialize ioloop by yourself or get it from framework.

Thanks for the quick reply :+1:

def get_client():
    from motor.motor_asyncio import AsyncIOMotorClient
    from sanic import app
    client = AsyncIOMotorClient("mongodb://rootuser:passme123@localhost:27045",io_loop=app.get_event_loop())
    return client

I changed the get_client() as this, and everything worked as expected. So is this the correct way?
Also, one doubt about calling the client function each time we need to access DB, is that a proper way to do it( I followed the example to test as such before digging around), or would it be better to define the client as a constant at the top along with app = Sanic(__name__)?

@sreecodeslayer I think it would be better to define constant at the top because it is calling it outside of the event loop.

Sitting inside a function handler that is.

Solution was provided and worked: https://github.com/channelcat/sanic/issues/749#issuecomment-304239758. Please close this issue.

I have tried the solution they comment but it is making connections all the time without reusing the previous ones, right?

It would not be easier to do this:

@app.listener('before_server_start')
def init(sanic, loop):
    global db
    from motor.motor_asyncio import AsyncIOMotorClient
    mongo_uri = "mongodb://127.0.0.1:27017/test"
    db = AsyncIOMotorClient(mongo_uri)['test']

So you can reuse the connection and the result of the requests is quite faster:

get_db()
Requests/sec:    580.71
Transfer/sec:     73.16KB

before_server_start
Requests/sec:   2174.00
Transfer/sec:    273.87KB

This is exactly the technique I was searching for. Solves my doubt regarding the efficiency of using the method over and over again.
Great one.

Would @kianxineki solutions work inside a blueprint scope? Like lets say I want to use the "db" object across blueprints so every blueprints can access a different database collection but doesn't need to create a new connection.

@andreheringer
I think I could do something similar to this:

models/__ init__.py

from motor.motor_asyncio import AsyncIOMotorClient

mongo_uri = "mongodb://127.0.0.1:27017/test"
db = AsyncIOMotorClient(mongo_uri)['database']

so you can do this anywhere

from models import db

does anyone on this thread have a public repo with some example code around this?
i have decent experience with node/express/mongo, but just new to python/sanic and some example code would be a big help to see some idiomatic patterns. any references appreciated!

@kianxineki would the above specifically need to use the io_loop trick from above like so?:

from motor.motor_asyncio import AsyncIOMotorClient
from sanic import app

mongo_uri = "mongodb://127.0.0.1:27017/test"
db = AsyncIOMotorClient(mongo_uri, io_loop=app.get_event_loop())['database']

from what i'm reading above, i thought motor need to be connected to sanic by event-loop and i don't see it in your sample, or are you making that "connection" elsewhere...?

__update__: nvm, i read this comment about get_event_loop in python 3.6 and how it will default to the running one. i kind of like explicitly passing via io_loop for clarity, but that's just me ;)

@tony-kerz

does anyone on this thread have a public repo with some example code around this?

I happen to have a repo that is quite old by now, you could still take a look at it Sanic Todo and relatively a new one, incomplete, having issues with Peewee and its orm. So its kinda dormant now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eseglem picture eseglem  路  4Comments

davidtgq picture davidtgq  路  3Comments

ubergarm picture ubergarm  路  4Comments

graingert picture graingert  路  3Comments

jasonab picture jasonab  路  3Comments