2016-05-25 4 views
9

asynchronous API私はいくつかのセットアップがあり、それを引き裂くSMTPサーバーにメールを接続して送信するために使用しています。したがって、Python 3のcontextlibcontextmanagerをうまく使用できます。非同期コンテキストマネージャ

しかし、両方ともジェネレータ構文を使用して書き込みを行うため、書き込みが可能かどうかわかりませんが、

これは問題を示している可能性があります(非同期呼び出しと歩留まりの違いをコンテキストマネージャに示すための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文を使用するのですか?そうでない場合、私はこれを達成するための別の方法がありますか?おそらく古いスタイルのコンテキストマネージャを使用していますか?

+0

'asyncio'は、非同期コンテキストWITH''非同期にも導入していますマネージャのプロトコルについては、https://www.python.org/dev/peps/pep-0492/#asynchronous-context-managers-and-async-with – jonrsharpe

+0

を参照してください。これは私が欲しいもののような外観です。私はチャンスを得るときにそれを実装するショットを与えるだろう。 – freebie

+1

3.7(時点2018のリリース)では、contextlibは '@ asynccontextmanager'を持っています –

答えて

9

@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(...) 

は私が愚かな何かをやった場合は指摘してお気軽ここ

は私はいくつかのサンプルコードですしたい人のためのように見えることになったものです。

import asyncio_extras 

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

は、Python < 3.6のために、あなたもasync_generatorパッケージを必要とするだろうし、await yield_(client)yield clientを置き換える:

+0

2度同時に使用すると、2番目のエンターのクライアントが最初のクライアントを上書きし、最初のクライアントを終了して2番目のクライアントを終了しますあまりにも。 – iScrE4m

+0

@ iScrE4mああ、私はそれが複数回使用されているとは予想せず、オンデマンドでオンデマンドでインスタンスを作成するだけです。おそらくラッパークラスを作成して、これを '__aenter__'と' __aexit__'を新しいインスタンスに委譲できますか? – freebie

4

asyncio_extrasパッケージには、このための良い解決策を持っています。

1

の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()