我有asynchronous API我用来连接并发送邮件到SMTP服务器,该服务器有一些设置并拆除它。因此,它非常适合使用Python 3 contextmanager
中的contextlib
。
虽然,我不知道它是否可以写,因为它们都使用生成器语法来编写。
这可能会证明问题(包含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中这种事情有可能吗?如果是,我将如何使用with
as
语句?如果没有,我可以用另一种方法实现这一点 - 也许使用旧式上下文管理器?
答案 0 :(得分:33)
在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()
答案 1 :(得分:15)
感谢@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(...)
如果我做了任何愚蠢的事,请随时指出。
答案 2 :(得分:9)
asyncio_extras包有一个很好的解决方案:
import asyncio_extras
@asyncio_extras.async_contextmanager
async def smtp_connection():
client = SMTPAsync()
...
对于Python&lt; 3.6,您还需要async_generator个包,并将yield client
替换为await yield_(client)
。