pythonasync-awaitpython-asyncioaio-mysql

Class variable is NoneType even though attribute was defined


I have a class that looks like this:

class DatabaseTest:

    def __init__(self):

        self.headers = {
            "host": os.environ["HOST"],
            "username": os.environ["USERNAME"],
            "password": os.environ["PASSWORD"]
        }

        self.db = None
        self.cursor = None
        asyncio.create_task(self._connect())

    
    async def _connect(self):
        
        self.db = await aiomysql.connect(self.headers['host'], self.headers['username'], self.headers['password'])
        self.cursor = await self.db.cursor()
        await self.cursor.execute('use bot')


    
    async def _insertData(self, guildid):

        sql = f'insert into data(guild) values ({guildid})'

        await self.cursor.execute(sql)

Whenever I do await DatabaseTest()._insertData(123456789) it gives an error saying that self.cursor is NoneType when I clearly redefined what self.cursor is in _connect. I know that _connect was called because I put a print statement right at the end and it printed. How would I fix this?

The traceback is this (im using it for a discord bot):

Traceback (most recent call last):
  File "C:\Users\Name\PycharmProjects\compscibot\venv\lib\site-packages\discord\ext\commands\core.py", line 85, in wrapped
    ret = await coro(*args, **kwargs)
  File "C:\Users\Name\PycharmProjects\compscibot\cogs\startup\Help.py", line 57, in cmdchannel
    channelid = await DatabaseTest().getData(ctx.guild.id,'cmdchannel')
  File "C:\Users\Name\PycharmProjects\compscibot\cogs\dbtest.py", line 75, in getData
    await self.cursor.execute(sql)
AttributeError: 'NoneType' object has no attribute 'execute'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\Name\PycharmProjects\compscibot\venv\lib\site-packages\discord\ext\commands\bot.py", line 903, in invoke
    await ctx.command.invoke(ctx)
  File "C:\Users\Name\PycharmProjects\compscibot\venv\lib\site-packages\discord\ext\commands\core.py", line 859, in invoke
    await injected(*ctx.args, **ctx.kwargs)
  File "C:\Users\Name\PycharmProjects\compscibot\venv\lib\site-packages\discord\ext\commands\core.py", line 94, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'NoneType' object has no attribute 'execute'
None

Solution

  • The context is not switched until await.

    asyncio.create_task() just schedules an execution.

    Using asyncio.sleep():

    db = DatabaseTest()
    await asyncio.sleep(0)
    db._insertData(123456789)
    

    or refactor:

    class DatabaseTest:
       def __init__(self):
            self.headers = {
                "host": os.environ["HOST"],
                "username": os.environ["USERNAME"],
                "password": os.environ["PASSWORD"]
            }
    
        async def __aenter__(self):
            await self._connect()
            return self
    
        async def __aexit__(self, exc_type, exc_val, exc_tb):
            # cleanup
            await self.cursor.close()
            self.db.close()
    
        ...
    
    async with DatabaseTest() as db:
        await db._insertData(123456789)