Python: How to use async with

How to use async with which can be used from python 3.5. PEP 492 -- Coroutines with async and await syntax

async with? An async version of the context manager.

  1. Make a connection to DB <-It takes time = async
  2. Do something with the connection you made
  3. Close the connection

When there is processing like

async with MyDB() as db:
    db.do_something()

If you make MyDB better by executing the code, you can automatically close the db connection when you exit the async with block.

Basic usage

For example, let's create a class that wraps aiomysql and use it.

my_db.py

my_db.py


import asyncio
import aiomysql


class MyDB:
    #Called just before entering async with.
    async def __aenter__(self):
        loop = asyncio.get_event_loop()
        self._connection = await aiomysql.connect(
            host='localhost',
            port=3306,
            user='root',
            password='ultrastrongpassword',
            db='my_db',
            charset='utf8',
            loop=loop
        )
        return self

    #Called immediately after exiting the async with block.
    async def __aexit__(self, exc_type, exc, tb):
        self._connection.close()

    async def fetchall(self, query, args=[]):
        cursor = await self._connection.cursor()
        await cursor.execute(query, args)
        return await cursor.fetchall()

It feels like hiding the connection destination of aiomysql.

The important ones are __aenter__ and __aexit__. By implementing these, this class can be used as an asynchronous context manager. The timing of being called is as commented.

As in the example, aenter is a common pattern for setting up resources and aexit for releasing resources.

User side

Let's also look at the caller.

Side to use


from my_db import MyDB


class Hoge:
    async def call_db(self):
        # db = MyDB()
        # async with db:
        #But it has the same meaning.
        async with MyDB() as db:
            result = db.fetchall('select * from some_table')
        print(result)

ʻAsync with [asynchronous context manager instance] as [return value of aenter]:` format.

When this is executed, the processing will proceed in the following order.

  1. An instance of MyDB is created (__init__ is called)
  2. Hoge's async with clause begins processing
  3. MyDB __aenter__ is called
  4. The return value of __aenter__ in MyDB is stored in db of ʻasync with as db`
  5. Hoge's result = db.fetchall ('select * from some_table') is executed
  6. MyDB __aexit__ is called

It feels like a db instance can use all its resources only when it's in async with.

Various other usages

I want to use it in the factory

ʻAsync with MyDB.connect () as db: `when you want to

Side to use


class Hoge:
    async def call_db(self):
        async with MyDB.connect() as db:
            result = db.fetchall('select * from some_table')
        print(result)

Just implement the connect method as you normally would:

my_db.py


class MyDB:
    __slots__ = ['_connection']

    async def __aenter__(self):
        (Abbreviation)

    async def __aexit__(self, exc_type, exc, tb):
        self._connection.close()

    async def fetchall(self, query, args=[]):
        (Abbreviation)

    @classmethod
    def connect(cls):
        return cls()

All you need is an instance of MyDB (asynchronous context manager) after async with.

I want to async with a system that doesn't return my own instance

Side to use


class Hoge:
    async def call_db(self):
        async with MyDB.direct_fetch('select * from some_table') as rows:
            print(rows)

In this case, the return value of MyDB.direct_fetch in async with is unlikely to be in the MyDB instance, so you need to think a little. Get aiohttp may be helpful.

It can be handled by creating a private context manager class as shown below.

my_db.py


class _QueryContextManager:
    def __init__(self, coro):
        self._coro = coro

    async def __aenter__(self):
        loop = asyncio.get_event_loop()
        self._connection = await aiomysql.connect(
            host='localhost',
            port=3306,
            user='root',
            password='ultrastrongpassword',
            db='my_db',
            charset='utf8',
            loop=loop
        )
        return await self._coro(self._connection)

    async def __aexit__(self, exc_type, exc, tb):
        self._connection.close()


class MyDB:
    __slots__ = ['_connection']

    @classmethod
    def direct_fetch(cls, query, args=[]):
        async def fetch(connection):
            cursor = await connection.cursor()
            await cursor.execute(query, args)
            return await cursor.fetchall()

        #Returns a private context manager.
        return _QueryContextManager(coro=fetch)

Hmm. I can't deny the feeling of force. .. There may be a better way to write it.

In aiohttp, the connection can be closed from the instance of the return value of request (self._resp.release ()), so it seems to be a little cleaner.

Recommended Posts

Python: How to use async with
How to use FTP with Python
python3: How to use bottle (2)
[Python] How to use list 1
How to use Python argparse
Python: How to use pydub
[Python] How to use checkio
[Python] How to use input ()
How to use Python lambda
[Python] How to use virtualenv
python3: How to use bottle (3)
python3: How to use bottle
How to use Python bytes
How to use tkinter with python in pyenv
[Python] How to use Pandas Series
How to use Requests (Python Library)
How to use SQLite in Python
How to use virtualenv with PowerShell
How to use python interactive mode with git bash
How to get started with Python
[Python] How to use list 3 Added
How to use Mysql in python
How to use OpenPose's Python API
How to use ChemSpider in Python
Python: How to use pydub (playback)
How to use PubChem in Python
How to calculate date with python
How to use python zip function
[Python] How to use Typetalk API
How to use Realsense's Python wrapper PyRealsense with Jetson Nano
[Python] Summary of how to use pandas
[Introduction to Python] How to use class in Python?
How to install and use pandas_datareader [Python]
How to use ManyToManyField with Django's Admin
How to use OpenVPN with Ubuntu 18.04.3 LTS
How to use Cmder with PyCharm (Windows)
[python] How to use __command__, function explanation
How to work with BigQuery in Python
[Introduction to Python] Let's use foreach with Python
[Python] How to use import sys sys.argv
How to use Ass / Alembic with HtoA
How to use Japanese with NLTK plot
How to do portmanteau test with python
[Python] Organizing how to use for statements
Memorandum on how to use gremlin python
How to display python Japanese with lolipop
[Python2.7] Summary of how to use unittest
How to use jupyter notebook with ABCI
python: How to use locals () and globals ()
How to use __slots__ in Python class
How to use CUT command (with sample)
How to use "deque" for Python data
How to enter Japanese with Python curses
How to use Python zip and enumerate
[Python] Understand how to use recursive functions
Summary of how to use Python list
How to use regular expressions in Python
[Python] How to deal with module errors
[Python2.7] Summary of how to use subprocess
How to use is and == in Python
[Blender x Python] How to use modifiers