asyncio: TypeError: 'coroutine' object is not subscriptable

Viewed 5762

Try make my code more asynchronous and get this eror:

find = (await loop.run_until_complete(a.finddb()[0])) TypeError: 'coroutine' object is not subscriptable

from telethon import TelegramClient, events, Button, utils, sync
import pymongo
from pymongo import TEXT
import re
import logging
import asyncio

class Search(): # search in mongodb
    def __init__(self, search): # connect to mongoDB
        self.search = search
        self.myclient = pymongo.MongoClient(
            "mongodb+srv://:@cluster0.ye4cx.mongodb.net/info?retryWrites=true&w=majority&ssl=true&ssl_cert_reqs=CERT_NONE")
        self.mydb = self.myclient["info"]
        self.mycol = self.mydb["comics"]


    async def searchdb(self): # finds all comics by request
        self.mycol.create_index([('title', TEXT)], default_language='english')
        self.find = self.mycol.find({"$text": {"$search": self.search}})
        if self.find.count() == 0:
            return 0
        else:
            return (self.find)

    async def finddb(self): # search info for 1 comics
        self.mycol.create_index([('title', TEXT)], default_language='english')
        self.find = self.mycol.find({"title": self.search})
        return (self.find)

@bot.on(events.NewMessage(pattern=r'(?<=|).*(?=|)')) # command for find comics info
async def find(event):
    loop = asyncio.get_event_loop()
    a = Search(event.text.replace("|", ""))
    find = await loop.run_until_complete(a.finddb()[0])
    await event.respond(f'**|{find.get("title")}|**\n\n**Статус перевода**: {find.get("status")}\n**Издатель**: {find.get("publisher")}\n\n**Жанр**: {find.get("genres")}\n**Описание**:\n{find.get("description")}', buttons=[[Button.inline('Ссылки на скачку',  b'next')]])

I try to use motor, but have same problem, but why it doesn't work? With pymongo it work perfect

New trouble find = (await a.finddb())[0] TypeError: 'AsyncIOMotorCursor' object is not subscriptable

from telethon import TelegramClient, events, Button, utils, sync
import re
import logging
import motor.motor_asyncio

class Search(): # search in mongodb
    def __init__(self, search): # connect to mongoDB
        self.search = search
        self.myclient = motor.motor_asyncio.AsyncIOMotorClient("mongodb+srv://login:pass@cluster0.ye4cx.mongodb.net/info?retryWrites=true&w=majority&ssl=true&ssl_cert_reqs=CERT_NONE")
        self.mydb = self.myclient["info"]
        self.mycol = self.mydb["comics"]



    async def searchdb(self): # finds all comics by request
        self.find = self.mycol.find({"$text": {"$search": self.search}})
        print(self.find)
        if self.find.count() == 0:
            return 0
        else:
            return (self.find)

    async def finddb(self): # search info for 1 comics
        self.find = self.mycol.find({"title": self.search})
        return (self.find)

@bot.on(events.NewMessage(pattern=r'(?<=|).*(?=|)')) # command for find comics info
async def find(event):
    a = Search(event.text.replace("|", ""))
    find = (await a.finddb())[0]
    print(find)
    await event.respond(f'**|{find.get("title")}|**\n\n**Статус перевода**: {find.get("status")}\n**Издатель**: {find.get("publisher")}\n\n**Жанр**: {find.get("genres")}\n**Описание**:\n{find.get("description")}', buttons=[[Button.inline('Ссылки на скачку',  b'next')]])
1 Answers

First let's start with some general notes. find is defined as a coroutine.

async def finddb(self):

You need to await it in order to execute it.

db = await self.finddb()

You can then index into its return value.

db[0]

If you'd like to continue to do this all in one line, you'll need to wrap it in parentheses.

(await self.finddb())[0]

Now onto your actual code. find is also a coroutine. You can't start an event loop inside one. When you call loop.run_until_complete, you're going to get a RuntimeError because the loop is already running. You also can't await run_until_complete as it isn't a coroutine. (await and run_until_complete are both ways to run a coroutine or task. The former is used inside a coroutine; the latter is used outside one.)

You can reduce your code to

find = (await a.finddb())[0]

You can also further simplify it by using PyMongo's find_one rather than find if you only ever care about the first document it returns.

Lastly, PyMongo itself is not asyncio-aware. You are effectively writing synchronous code with the added overhead of an event loop. If you'd like to potentially benefit from asyncio, you should look at using Motor instead.

Related