簡體   English   中英

如何在程序從庫 POV 結束之前自動關閉 aiohttp.ClientSession?

[英]How to close aiohttp.ClientSession automatically before program ends from a library POV?

我正在用 aiohttp 構建一個異步庫。 該庫有一個客戶端,它在實例化時創建一個ClientSession並使用它向 API 發出請求(它是一個 REST API 包裝器)

我面臨的問題是如何在退出時干凈地關閉客戶端 session?

如果 session 未明確關閉,則會出現很多錯誤,但我不能簡單地使用上下文管理器關閉 session,因為我不知道程序何時結束。

一個典型的用途是這樣的:

from mylibrary import Client
client = Client()

async main():
  await client.get_foo(...)
  await client.patch_bar(...)

asyncio.run(main())

我可以在main上添加await client.close_session()但我想從最終用戶那里移除這種責任,所以理想情況下客戶端會在程序結束時自動關閉ClientSession

我怎樣才能做到這一點?

我曾嘗試在客戶端上使用__del__來獲取循環並關閉 session 以及使用atexit庫,但沒有成功,但似乎在這些運行時 asyncio 循環已經被破壞,我仍然收到警告。

具體錯誤是:

Fatal error on SSL transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x0000013ACFD54AF0>
transport: <_ProactorSocketTransport fd=1052 read=<_OverlappedFuture cancelled>>

我對這個錯誤進行了一些研究,谷歌似乎認為這是因為我需要實現流控制,但是我有這個錯誤只有在我沒有明確關閉 session 時才會發生。

不幸的是,似乎唯一可以應用的干凈模式是使您的客戶端本身成為(異步)上下文管理器,並要求您的用戶在with塊中使用它。

__del__方法在某些情況下可以工作——但它要求用戶的代碼不會“泄露”客戶端實例本身。

所以,代碼很簡單——用戶的負擔不是零:


class Client:
    ...
    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_value, tb):
        await self.close_session()

loop.stop上創建一個偽鈎子:另一種方法,雖然不是“干凈”並且不能保證工作,但可能是裝飾正在運行的循環stop function 以添加對 close_session 的調用。

如果用戶代碼只是“停止”並且沒有正確關閉循環,那么這無論如何都無濟於事 - 但我想這可能是“行為良好”用戶的一個選擇。

這里的大問題是沒有記錄 - 但是選擇 asyncio 內部,它看起來總是會 go 通過self.stop()

import asyncio

class ShutDownCb:
    def __init__(self, cb):
        self.cb = cb
        self.stopping = False
        loop = self.loop = asyncio.get_running_loop()
        self.original_stop = loop.stop
        loop.stop = self.new_stop

    async def _stop(self):
        self.task.result()
        return self.original_stop()

    def new_stop(self):
        if not self.stopping:
            self.stopping = True
            self.task = asyncio.create_task(self.cb())
            asyncio.create_task(self._stop())
            return
            
        return self.original_stop()


class Client:
    def __init__(self, ...):
        ...
        ShutDownCb(self.close_session)


暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM