非同步上下文管理

2024-05-15 21:13:53 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个asynchronous API用来连接和发送邮件到一个SMTP服务器,这个服务器有一些设置并可以拆卸。因此它非常适合使用Python 3的contextlib中的contextmanager

不过,我不知道是否可以写,因为它们都使用生成器语法来写。

这可能会演示问题(包含yield base和async await语法的混合,以演示异步调用和上下文管理器的yield之间的区别)。

@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语句?如果不是的话,有没有其他的方法我可以做到这一点-也许使用旧风格的上下文管理器?


Tags: 服务器clientapi管理器baseasync语法邮件
3条回答

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)

在Python3.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版本出来之前,您可以使用^{}包来实现这一点。在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()

感谢@jornsharpe能够创建一个异步上下文管理器。

下面是我的一些代码示例:

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

如果我做了什么蠢事,请随时指出。

相关问题 更多 >