web-dev-qa-db-ja.com

非同期コンテキストマネージャー

非同期API があり、これを使用してSMTPサーバーにメールを接続して送信します。したがって、Python 3のcontextmanager。からのcontextlibを使用することにうまく適合します。

ただし、どちらもジェネレーター構文を使用して記述するため、書き込みが可能かどうかはわかりません。

これは問題を示すかもしれません(非同期呼び出しとコンテキストマネージャーへのyieldの違いを示すためにyield-baseとasync-await構文が混在しています)。

@contextmanager
async def smtp_connection():
    client = SMTPAsync()
    ...

    try:
        await client.connect(smtp_url, smtp_port)
        await client.starttls()
        await client.login(smtp_username, smtp_password)
        yield client
    finally:
        await client.quit()

このようなことはpython現在ですか?そしてwithasステートメントが使用されている場合、どのように使用しますか?これ-多分古いスタイルのコンテキストマネージャを使用している?

28
freebie

@jonrsharpeのおかげで、非同期コンテキストマネージャーを作成できました。

ここに、サンプルコードが必要な人にとって、私のように見えるものがあります。

class SMTPConnection():
    def __init__(self, url, port, username, password):
        self.client   = SMTPAsync()
        self.url      = url
        self.port     = port
        self.username = username
        self.password = password

    async def __aenter__(self):
        await self.client.connect(self.url, self.port)
        await self.client.starttls()
        await self.client.login(self.username, self.password)

        return self.client

    async def __aexit__(self, exc_type, exc, tb):
        await self.client.quit()

使用法:

async with SMTPConnection(url, port, username, password) as client:
    await client.sendmail(...)

バカなことをしてしまったら、気軽に指摘してください。

18
freebie

Python 3.7では、次のように記述できます。

from contextlib import asynccontextmanager

@asynccontextmanager
async def smtp_connection():
    client = SMTPAsync()
    ...

    try:
        await client.connect(smtp_url, smtp_port)
        await client.starttls()
        await client.login(smtp_username, smtp_password)
        yield client
    finally:
        await client.quit()

3.7が出るまで、 async_generator このパッケージ。 3.6では、次のように書くことができます。

# This import changed, everything else is the same
from async_generator import asynccontextmanager

@asynccontextmanager
async def smtp_connection():
    client = SMTPAsync()
    ...

    try:
        await client.connect(smtp_url, smtp_port)
        await client.starttls()
        await client.login(smtp_username, smtp_password)
        yield client
    finally:
        await client.quit()

そして、3.5までさかのぼって作業したい場合は、次のように書くことができます。

# This import changed again:
from async_generator import asynccontextmanager, async_generator, yield_

@asynccontextmanager
@async_generator      # <-- added this
async def smtp_connection():
    client = SMTPAsync()
    ...

    try:
        await client.connect(smtp_url, smtp_port)
        await client.starttls()
        await client.login(smtp_username, smtp_password)
        await yield_(client)    # <-- this line changed
    finally:
        await client.quit()
37

asyncio_extras パッケージには、これに対する素晴らしいソリューションがあります:

_import asyncio_extras

@asyncio_extras.async_contextmanager
async def smtp_connection():
    client = SMTPAsync()
    ...
_

Python <3.6の場合、 async_generator パッケージも必要で、_yield client_をawait yield_(client)に置き換えます。

9
Bart Robinson