事件迴圈

原始碼:Lib/asyncio/events.pyLib/asyncio/base_events.py


前言

事件迴圈是每個 asyncio 應用程式的核心。事件迴圈執行非同步任務和回呼、執行網路 IO 操作並啟動子行程。

應用程式開發人員通常應使用高階的 asyncio 函式,例如asyncio.run(),並且很少需要參照事件迴圈物件或呼叫其方法。本節主要針對那些需要更細粒度控制事件迴圈行為的低階程式碼、函式庫和框架的作者。

取得事件迴圈

以下的低階函式可用於取得、設置或建立事件迴圈:

asyncio.get_running_loop()

在目前作業系統執行緒中回傳正在運行的事件迴圈。

如果沒有運行的事件迴圈,則引發RuntimeError

此函式只能從協程或回呼函式中呼叫。

在 3.7 版被加入.

asyncio.get_event_loop()

取得目前的事件迴圈。

當從協程或回呼函式呼叫此函式(例如,使用 call_soon 或類似的 API 於排程呼叫),此函式將永遠回傳正在運行的事件迴圈。

如果沒有設定正在運行的事件迴圈,該函式將回傳get_event_loop_policy().get_event_loop() 呼叫的結果。

由於此函式具有相當複雜的行為(尤其是在使用自訂事件迴圈策略時),在協程和回呼函式中,建議使用get_running_loop() 函式,而不是get_event_loop()

如上所述,可以考慮使用高階的asyncio.run() 函式,而不是使用這些較低階的函式手動建立和關閉事件迴圈。

在 3.12 版之後被棄用:如果沒有目前事件迴圈,則會發出棄用警告。在未來的某個 Python 發行版中,這將變成錯誤。

asyncio.set_event_loop(loop)

loop 設置為目前 OS 執行緒的目前事件迴圈。

asyncio.new_event_loop()

建立並回傳新的事件迴圈物件。

請注意get_event_loop()set_event_loop()new_event_loop() 函式的行為可以透過設定自訂事件迴圈策略進行調整。

目錄

本頁文件包含以下章節:

事件迴圈方法

事件迴圈提供以下低階 API:

啟動和停止迴圈

loop.run_until_complete(future)

運行直到future (一個Future 實例)完成。

如果引數是協程物件,則它將被隱式排程為asyncio.Task 運行。

回傳 Future 的結果或引發其例外。

loop.run_forever()

運行事件迴圈直到stop() 被呼叫。

如果在呼叫run_forever() 之前呼叫stop(),則迴圈將使用超時為零的方式輪詢 I/O 選擇器,運行所有回應 I/O 事件(以及已經排程的事件)的回呼,然後退出。

如果在run_forever() 運行時呼叫stop(),則迴圈將運行目前批次的回呼函式,然後退出。請注意,由回呼函式排程的新回呼在此情況下不會運行;而是在下次呼叫run_forever()run_until_complete() 時運行。

loop.stop()

停止事件迴圈。

loop.is_running()

如果事件迴圈目前正在運行,則回傳True

loop.is_closed()

如果事件迴圈已關閉,則回傳True

loop.close()

關閉事件迴圈。

不得於迴圈運行中呼叫此函式。將丟棄任何待處理的回呼。

此方法清除所有佇列並關閉執行器,但不等待執行器完成。

此方法是冪等且不可逆的。在事件迴圈關閉後不應呼叫其他方法。

asyncloop.shutdown_asyncgens()

排程所有目前打開的非同步產生器物件使用aclose() 呼叫來關閉。呼叫此方法後,如果疊代新的非同步產生器,事件迴圈將發出警告。應該使用此方法可靠地完成所有已排程的非同步產生器。

請注意,使用asyncio.run() 時不需要呼叫此函式。

範例:

try:loop.run_forever()finally:loop.run_until_complete(loop.shutdown_asyncgens())loop.close()

在 3.6 版被加入.

asyncloop.shutdown_default_executor(timeout=None)

排程預設執行器的關閉,並等待它加入ThreadPoolExecutor 中的所有執行緒。一旦呼叫了此方法,使用預設執行器與loop.run_in_executor() 將引發RuntimeError

timeout 參數指定執行器完成加入所需的時間(以float 秒為單位)。預設情況下為None,不會限制執行器所花費的時間。

如果達到timeout,將發出RuntimeWarning 警告,預設執行器將立即終止,不等待其執行緒完成加入。

備註

使用asyncio.run() 時請勿呼叫此方法,因為後者會自動處理預設執行器的關閉。

在 3.9 版被加入.

在 3.12 版的變更:加入timeout 參數。

排程回呼函式

loop.call_soon(callback,*args,context=None)

在事件迴圈的下一次疊代中排程以args 引數呼叫callbackcallback

回傳asyncio.Handle 的實例,稍後可以用於取消回呼函式。

回呼函式按照其註冊的順序呼叫。每個回呼函式將被呼叫恰好一次。

選用的僅限關鍵字引數context 指定了要給callback 執行的自定義contextvars.Context。當未提供context 時,回呼函式使用目前情境。

call_soon_threadsafe() 不同,此方法不是執行緒安全的。

loop.call_soon_threadsafe(callback,*args,context=None)

這是call_soon() 的執行緒安全變體。當從另一個執行緒排程回呼函式時,必須使用此函式,因為call_soon() 不是執行緒安全的。

This function is safe to be called from a reentrant context or signal handler,however, it is not safe or fruitful to use the returned handle in such contexts.

如果在已關閉的迴圈上呼叫,則引發RuntimeError。在主應用程式關閉時,這可能發生在次要執行緒上。

請參閱文件的並行和多執行緒部分。

在 3.7 版的變更:新增了context 僅限關鍵字參數。詳細資訊請參閱PEP 567

備註

大多數asyncio 排程函式不允許傳遞關鍵字引數。要傳遞關鍵字引數,請使用functools.partial()

# 將會排程 "print("Hello", flush=True)"loop.call_soon(functools.partial(print,"Hello",flush=True))

通常使用 partial 物件比使用 lambda 更方便,因為 asyncio 可以在除錯和錯誤訊息中更好地呈現 partial 物件。

排程延遲的回呼函式

事件迴圈提供為回呼函式排程在將來某個時間點才呼叫的機制。事件迴圈使用了單調時鐘來追蹤時間。

loop.call_later(delay,callback,*args,context=None)

排程callback 在給定的delay 秒數後呼叫(可以是整數或浮點數)。

回傳asyncio.TimerHandle 的實例,可用於取消回呼函式。

callback 將只被呼叫恰好一次。如果有兩個回呼函式被排程在完全相同的時間,則其呼叫順序是不定的。

可選的位置引數args 將在呼叫回呼函式時傳遞。如果要使用關鍵字引數呼叫回呼函數,請使用functools.partial()

可選的僅限關鍵字context 引數允許為callback 指定自定義的contextvars.Context 以提供運行。當未提供context 時,將使用目前情境。

在 3.7 版的變更:新增了context 僅限關鍵字參數。詳細資訊請參閱PEP 567

在 3.8 版的變更:在 Python 3.7 及更早版本中,使用預設事件迴圈實作時,delay 不能超過一天。這在 Python 3.8 中已經修復。

loop.call_at(when,callback,*args,context=None)

排程callback 在給定的絕對時間戳when (整數或浮點數)處呼叫,使用與loop.time() 相同的時間參照。

此方法的行為與call_later() 相同。

回傳asyncio.TimerHandle 的實例,可用於取消回呼函式。

在 3.7 版的變更:新增了context 僅限關鍵字參數。詳細資訊請參閱PEP 567

在 3.8 版的變更:在 Python 3.7 及更早版本中,使用預設事件迴圈實作時,when 和目前時間之間的差值不能超過一天。這在 Python 3.8 中已經修復。

loop.time()

根據事件迴圈的內部單調時鐘,回傳目前時間,以float 值表示。

備註

在 3.8 版的變更:在 Python 3.7 及更早版本中,超時(相對delay 或絕對when)不應超過一天。這在 Python 3.8 中已經修復。

也參考

函式asyncio.sleep()

建立 Futures 和 Tasks

loop.create_future()

建立附加到事件迴圈的asyncio.Future 物件。

這是在 asyncio 中建立 Futures 的首選方式。這允許第三方事件迴圈提供 Future 物件的替代實作(具有更好的性能或儀器計測表現)。

在 3.5.2 版被加入.

loop.create_task(coro,*,name=None,context=None,**kwargs)

排程執行協程coro。回傳Task 物件。

第三方事件迴圈可以使用其自己的Task 子類別以實現互操作性(interoperability)。在這種情況下,結果類型是Task 的子類別。

The full function signature is largely the same as that of theTask constructor (or factory) - all of the keyword arguments tothis function are passed through to that interface, exceptname,orcontext if it isNone.

如果提供了name 引數且不為None,則將其設置為任務的名稱,使用Task.set_name()

可選的僅限關鍵字context 引數允許為coro 指定自定義的contextvars.Context 以提供運行。當未提供context 時,將建立目前情境的副本。

在 3.8 版的變更:加入name 參數。

在 3.11 版的變更:加入context 參數。

在 3.13.3 版的變更:Addedkwargs which passes on arbitrary extra parameters, includingname andcontext.

在 3.13.4 版的變更:Rolled back the change that passes onname andcontext (if it is None),while still passing on other arbitrary keyword arguments (to avoid breaking backwards compatibility with 3.13.3).

loop.set_task_factory(factory)

設置將由loop.create_task() 使用的任務工廠。

如果factoryNone,將設置預設的任務工廠。否則,factory 必須是一個具有匹配簽名(loop,coro,**kwargs)callable,其中loop 是有效事件迴圈的參照、coro 是一個協程物件。該可呼叫物件必須傳遞所有kwargs 並回傳一個與asyncio.Task 相容的物件。

在 3.13.3 版的變更:Required that allkwargs are passed on toasyncio.Task.

在 3.13.4 版的變更:name is no longer passed to task factories.context is no longer passedto task factories if it isNone.

loop.get_task_factory()

回傳任務工廠,如果使用預設任務工廠則回傳None

打開網路連線

asyncloop.create_connection(protocol_factory,host=None,port=None,*,ssl=None,family=0,proto=0,flags=0,sock=None,local_addr=None,server_hostname=None,ssl_handshake_timeout=None,ssl_shutdown_timeout=None,happy_eyeballs_delay=None,interleave=None,all_errors=False)

打開以hostport 指定之給定地址的串流傳輸連線。

根據host*(或提供的 *family 引數)的情況,socket 家族可以是AF_INETAF_INET6

Socket 類型將為SOCK_STREAM

protocol_factory 必須是一個回傳asyncio protocol 實作的可呼叫函式。

此方法將嘗試在背景建立連線。成功時,它將回傳一對(transport,protocol)

底層操作的時間軸簡介如下:

  1. 建立連線並為其建立傳輸

  2. protocol_factory 在無引數的情況下被呼叫,並且預計回傳一個協定 實例。

  3. 通過呼叫其connection_made() 方法,將協議實例與傳輸連線在一起。

  4. 成功時回傳一個(transport,protocol) 元組。

建立的傳輸是一個依賴實作的雙向串流。

其他引數:

  • 若有給定ssl 且非 false,將建立 SSL/TLS 傳輸(預設建立普通 TCP 傳輸)。如果sslssl.SSLContext 物件,則使用該情境來建立傳輸;如果sslTrue,則使用ssl.create_default_context() 回傳的預設情境。

  • server_hostname 設置或覆蓋目標伺服器憑證將匹配的主機名稱。僅在ssl 不為None 時傳遞。預設情況下,將使用host 引數的值。如果host 為空,則沒有預設值,必須傳遞server_hostname 的值。若server_hostname 為空字串,將停用主機名稱匹配(這是一個嚴重的安全風險,可能導致中間人攻擊)。

  • familyprotoflags 是可選的位址家族、協議和旗標,用於傳遞至 getaddrinfo() 進行host 解析。若有給定這些應該都是相應socket 模組常數的整數。

  • 若有給定,happy_eyeballs_delay 會啟用此連線的 Happy Eyeballs。它應該是一個浮點數,表示等待連線嘗試完成的秒數,然後在並行啟動下一次嘗試。這是RFC 8305 中定義的「連線嘗試延遲」。RFC 建議的合理預設值為0.25 秒(250 毫秒)。

  • interleave 控制主機名稱解析為多個 IP 位址時的地址重新排序。若為0 或未指定,將不執行重排序,並按getaddrinfo() 回傳的順序嘗試位址。如果指定正整數,則按地址家族交錯排列,給定的整數直譯為RFC 8305 中定義的「首個地址家族計數」。如果未指定happy_eyeballs_delay,則預設值為0,如果指定則為1

  • 若有給定sock 則其應為已存在且已連線的socket.socket 物件,可供傳輸使用。如果提供了sock,則不應指定hostportfamilyprotoflagshappy_eyeballs_delayinterleavelocal_addr 中的任何一項。

    備註

    引數sock 將 socket 所有權轉移給所建立的傳輸 socket,請呼叫傳輸的close() 方法。

  • 若有給定local_addr 則其為一個(local_host,local_port) 元組,用於在本地綁定 socket。將使用getaddrinfo() 查找local_hostlocal_port,方式類似於hostport

  • ssl_handshake_timeout (對於 TLS 連線)是等待 TLS 交握的時間,以秒為單位,在那之前若未完成則會中斷連線。如果為None (預設值),則會等待60.0 秒。

  • ssl_shutdown_timeout 是等待 SSL 關閉完成以前中斷連線的時間,以秒為單位。如果為None (預設值),則會等待30.0 秒。

  • all_errors 決定在無法建立連線時會引發哪些例外。預設情況下,只會引發單一Exception:如果只有一個例外或所有錯誤訊息相同,則引發第一個例外,否則引發包含所有錯誤訊息的單一OSError。當all_errorsTrue 時,將引發包含所有例外的ExceptionGroup (即使只有一個例外)。

在 3.5 版的變更:新增ProactorEventLoop 中的 SSL/TLS 支援。

在 3.6 版的變更:所有 TCP 連線都預設有socket.TCP_NODELAY socket 選項。

在 3.7 版的變更:增加ssl_handshake_timeout 參數。

在 3.8 版的變更:加入happy_eyeballs_delayinterleave 參數。

Happy Eyeballs 演算法:雙協定堆疊主機 (Dual-Stack Hosts) 的成功。當伺服器的 IPv4 路徑和協議運作正常,但伺服器的 IPv6 路徑和協議不運作時,雙棧用戶端應用程式會比僅具 IPv4 的用戶端體驗到顯著的連線延遲。這是不希望的,因為這會導致雙棧用戶端的使用者體驗變差。本文件具體說明了減少此用戶可見延遲的演算法要求並提供了一種演算法。

更多資訊請見:https://datatracker.ietf.org/doc/html/rfc6555

在 3.11 版的變更:增加ssl_shutdown_timeout 參數。

在 3.12 版的變更:已新增all_errors

也參考

函式open_connection() 是高階的替代 API。它回傳一對 (StreamReader,StreamWriter) 可直接在 async/await 程式碼中使用。

asyncloop.create_datagram_endpoint(protocol_factory,local_addr=None,remote_addr=None,*,family=0,proto=0,flags=0,reuse_port=None,allow_broadcast=None,sock=None)

建立一個資料報連線。

Socket 家族可以是AF_INETAF_INET6AF_UNIX,視乎host(或提供的family 引數)而定。

Socket 類型將為SOCK_DGRAM

protocol_factory 必須是可呼叫的函式,回傳protocol 實作。

成功時回傳(transport,protocol) 元組。

其他引數:

  • local_addr,如果提供,是一個(local_host,local_port) 元組,用於在本地綁定 socket。local_hostlocal_port 使用getaddrinfo() 來查找。

  • remote_addr,如果提供,是一個(remote_host,remote_port) 元組,用於將 socket 連線到遠端位址。remote_hostremote_port 使用getaddrinfo() 來查找。

  • familyprotoflags 是用於傳遞給getaddrinfo() 以解析host 的可選地址家族、協定和旗標。如果提供,這些應該都是來自相應的socket 模組常數的整數。

  • reuse_port 告訴核心允許將此端點綁定到與其他現有端點相同的埠,只要它們在建立時都設定了此旗標。此選項不受 Windows 和某些 Unix 系統支援。如果未定義SO_REUSEPORT 常數,則不支援此功能。

  • allow_broadcast 告訴核心允許此端點向廣播位址發送訊息。

  • sock 可以選擇性地指定,以使用預先存在且已連線的socket.socket 物件供傳輸使用。如果指定,local_addrremote_addr 應省略(必須是None)。

    備註

    引數sock 將 socket 所有權轉移給所建立的傳輸 socket,請呼叫傳輸的close() 方法。

請參閱UDP 回應用戶端協議UDP 回應伺服器協議 範例。

在 3.4.4 版的變更:新增familyprotoflagsreuse_addressreuse_portallow_broadcastsock 參數。

在 3.8 版的變更:新增對於 Windows 的支援。

在 3.8.1 版的變更:不再支援reuse_address 參數,因為使用SO_REUSEADDR 對於 UDP 存有重大的安全疑慮。明確傳遞reuse_address=True 將引發例外。

當具有不同 UID 的多個行程使用SO_REUSEADDR 將 socket 分配給相同的 UDP socket 地址時,傳入的封包可能會在 socket 之間隨機分佈。

對於有支援的平台,reuse_port 可以用作類似功能的替代方案。使用reuse_port,將改為使用SO_REUSEPORT,該選項明確禁止具有不同 UID 的行程將 socket 分配給相同的 socket 地址。

在 3.11 版的變更:自 Python 3.9.0、3.8.1、3.7.6 和 3.6.10 起,已完全移除reuse_address 參數。

asyncloop.create_unix_connection(protocol_factory,path=None,*,ssl=None,sock=None,server_hostname=None,ssl_handshake_timeout=None,ssl_shutdown_timeout=None)

建立一個 Unix 連線。

Socket 家族將為AF_UNIX;socket 類型將為SOCK_STREAM

成功時回傳(transport,protocol) 元組。

path 是 Unix 域 socket 的名稱,除非指定sock 參數,否則為必填。支援抽象 Unix sockets、strbytesPath 路徑。

有關此方法的引數資訊,請參閱loop.create_connection() 方法的文件。

適用: Unix.

在 3.7 版的變更:新增ssl_handshake_timeout 參數。path 參數現在可以是path-like object

在 3.11 版的變更:增加ssl_shutdown_timeout 參數。

建立網路伺服器

asyncloop.create_server(protocol_factory,host=None,port=None,*,family=socket.AF_UNSPEC,flags=socket.AI_PASSIVE,sock=None,backlog=100,ssl=None,reuse_address=None,reuse_port=None,keep_alive=None,ssl_handshake_timeout=None,ssl_shutdown_timeout=None,start_serving=True)

建立一個 TCP 伺服器(socket 類型SOCK_STREAM),監聽host 位址的port

回傳一個Server 物件。

引數:

  • protocol_factory 必須是可呼叫的函式,回傳protocol 實作。

  • 可以將host 參數設為幾種類型,以確定伺服器將監聽的位置:

    • 如果host 是字串,則 TCP 伺服器綁定到由host 指定的單個網路介面。

    • 如果host 是字串序列,則 TCP 伺服器綁定到序列指定的所有網路介面。

    • host 是空字串或None,則所有介面都被假定並回傳多個 socket 的清單(可能一個用於 IPv4,另一個用於 IPv6)。

  • 可以設定port 參數以指定伺服器應該監聽的埠。如果是0None(預設值),將隨機選擇一個未使用的埠(請注意,如果host 解析為多個網路介面,將為每個介面隨機選擇不同的隨機埠)。

  • family 可以設定為socket.AF_INETAF_INET6 以強制使用 IPv4 或 IPv6。如果未設定,family 將從主機名稱決定(預設為AF_UNSPEC)。

  • flagsgetaddrinfo() 的位元遮罩。

  • 可以可選地指定sock 以使用現有的 socket 物件。如果指定了,hostport 不能指定。

    備註

    sock 引數將 socket 的所有權轉移給建立的伺服器。要關閉 socket,請呼叫伺服器的close() 方法。

  • backlog 是傳遞給listen() 的最大佇列連線數(預設為 100)。

  • ssl 可以設定為SSLContext 實例以在接受的連線上啟用 TLS。

  • reuse_address 告訴內核重用TIME_WAIT 狀態下的本地 socket,而不等待其自然超時過期。如果未指定,在 Unix 上將自動設置為True

  • reuse_port 告訴內核允許此端點繫結到與其他現有端點繫結的相同埠,只要它們在建立時都設置了此旗標。此選項在旗標 Windows 上不受支援。

  • keep_alive 設為True 透過啟用定期的訊息傳輸來保持連線活躍。

在 3.13 版的變更:加入keep_alive 參數。

  • (對於 TLS 伺服器)ssl_handshake_timeout 是在中斷連線之前等待 TLS 握手完成的時間(以秒為單位)。如果為None(預設),則為60.0 秒。

  • ssl_shutdown_timeout 是等待 SSL 關閉完成以前中斷連線的時間,以秒為單位。如果為None (預設值),則會等待30.0 秒。

  • start_serving 設置為True(預設)將使建立的伺服器立即開始接受連接。當設置為False 時,用戶應該等待Server.start_serving()Server.serve_forever() 來使伺服器開始接受連線。

在 3.5 版的變更:新增ProactorEventLoop 中的 SSL/TLS 支援。

在 3.5.1 版的變更:host 參數可以是字串序列。

在 3.6 版的變更:新增ssl_handshake_timeoutstart_serving 參數。所有 TCP 連線都預設有socket.TCP_NODELAY socket 選項。

在 3.11 版的變更:增加ssl_shutdown_timeout 參數。

也參考

start_server() 函式是一個更高階的替代 API,它回傳一對StreamReaderStreamWriter,可以在 async/await 程式碼中使用。

asyncloop.create_unix_server(protocol_factory,path=None,*,sock=None,backlog=100,ssl=None,ssl_handshake_timeout=None,ssl_shutdown_timeout=None,start_serving=True,cleanup_socket=True)

類似loop.create_server(),但適用於AF_UNIX socket 家族。

path 是 Unix 域 socket 的名稱,除非提供了sock 引數,否則必須給定。支援抽象 Unix sockets、strbytesPath 路徑。

如果cleanup_socket 為真,則 Unix socket 將在伺服器關閉時自動從檔案系統中刪除,除非在建立伺服器後替換了 socket。

有關此方法的引數資訊,請參閱loop.create_server() 方法的文件。

適用: Unix.

在 3.7 版的變更:新增ssl_handshake_timeoutstart_serving 參數。path 參數現在可為一個Path 物件。

在 3.11 版的變更:增加ssl_shutdown_timeout 參數。

在 3.13 版的變更:加入cleanup_socket 參數。

asyncloop.connect_accepted_socket(protocol_factory,sock,*,ssl=None,ssl_handshake_timeout=None,ssl_shutdown_timeout=None)

將已接受的連線包裝成傳輸層/協議對。

此方法可以由在 asyncio 外接受連線但使用 asyncio 處理連線的伺服器使用。

參數:

  • protocol_factory 必須是可呼叫的函式,回傳protocol 實作。

  • sock 是從socket.accept 回傳的預先存在的 socket 物件。

    備註

    引數sock 將 socket 所有權轉移給所建立的傳輸 socket,請呼叫傳輸的close() 方法。

  • ssl 可以設置為SSLContext 以在已接受的連線上啟用 SSL。

  • (對於 SSL 連線)ssl_handshake_timeout 是在中斷連線之前等待 SSL 握手完成的時間(以秒為單位)。如果為None(預設),則為60.0 秒。

  • ssl_shutdown_timeout 是等待 SSL 關閉完成以前中斷連線的時間,以秒為單位。如果為None (預設值),則會等待30.0 秒。

回傳(transport,protocol) 對。

在 3.5.3 版被加入.

在 3.7 版的變更:增加ssl_handshake_timeout 參數。

在 3.11 版的變更:增加ssl_shutdown_timeout 參數。

傳輸檔案

asyncloop.sendfile(transport,file,offset=0,count=None,*,fallback=True)

通過transport 發送file。回傳發送的總位元組數。

如果可用,該方法使用高性能os.sendfile()

file 必須是以二進位模式打開的常規檔案物件。

offset 告訴從哪裡開始讀取檔案。如果指定了,count 是要傳輸的總位元組數,而不是發送檔案直到達到 EOF。即使此方法引發錯誤時,檔案位置也始終更新,可以使用file.tell() 取得實際發送的位元組數。

fallback 設置為True 會使 asyncio 在平台不支援 sendfile 系統呼叫時(例如 Windows 或 Unix 上的 SSL socket)手動讀取和發送檔案。

如果系統不支援sendfile 系統呼叫且fallbackFalse,則引發SendfileNotAvailableError

在 3.7 版被加入.

TLS 升級

asyncloop.start_tls(transport,protocol,sslcontext,*,server_side=False,server_hostname=None,ssl_handshake_timeout=None,ssl_shutdown_timeout=None)

將基於傳輸的現有連線升級到 TLS。

建立 TLS 編解碼器實例並在transportprotocol 之間插入它。編解碼器既實作了對於transport 的協議,也實作了對於protocol 的傳輸。

回傳建立的雙介面實例。在await 後,protocol 必須停止使用原始的transport,僅與回傳的物件通信,因為編碼器快取了protocol 端的資料,並且與transport 間歇性地交換額外的 TLS session 封包。

在某些情況下(例如傳入的傳輸已經關閉),此函式可能回傳None

參數:

  • transportprotocol 實例,由像create_server()create_connection() 等方法回傳。

  • sslcontext:配置好的SSLContext 實例。

  • 當升級伺服器端連線時(像由create_server() 建立的那樣)傳遞True

  • server_hostname:設置或覆蓋將用於匹配目標伺服器憑證的主機名。

  • ssl_handshake_timeout (對於 TLS 連線)是等待 TLS 交握的時間,以秒為單位,在那之前若未完成則會中斷連線。如果為None (預設值),則會等待60.0 秒。

  • ssl_shutdown_timeout 是等待 SSL 關閉完成以前中斷連線的時間,以秒為單位。如果為None (預設值),則會等待30.0 秒。

在 3.7 版被加入.

在 3.11 版的變更:增加ssl_shutdown_timeout 參數。

監視檔案描述器

loop.add_reader(fd,callback,*args)

開始監視fd 檔案描述器的讀取可用性,一但fd 可讀取,使用指定引數叫用callback

任何預先存在、為fd 註冊的回呼函式將被取消並替換為callback

loop.remove_reader(fd)

停止監視fd 檔案描述器的讀取可用性。如果fd 之前正在監視讀取,則回傳True

loop.add_writer(fd,callback,*args)

開始監視fd 檔案描述器的寫入可用性,一旦fd 可寫入,使用指定引數叫用callback

任何預先存在、為fd 註冊的回呼函式將被取消並替換為callback

使用functools.partial()callback傳送關鍵字引數

loop.remove_writer(fd)

停止監視fd 檔案描述器的寫入可用性。如果fd 之前正在監視寫入,則回傳True

另請參閱平台支援部分以了解這些方法的一些限制。

直接使用 socket 物件

一般情況下,使用基於傳輸的 API(如loop.create_connection()loop.create_server())的協議實作比直接使用 socket 的實作更快。然而在某些情況下性能不是關鍵,直接使用socket 物件更方便。

asyncloop.sock_recv(sock,nbytes)

sock 接收最多nbytessocket.recv() 的非同步版本。

將接收到的資料作為 bytes 物件回傳。

sock 必須是非阻塞 socket。

在 3.7 版的變更:儘管此方法一直記錄為協程方法,但 Python 3.7 之前的版本回傳Future。自 Python 3.7 起,這是asyncdef 方法。

asyncloop.sock_recv_into(sock,buf)

sock 接收資料到buf 緩衝區。仿照阻塞socket.recv_into() 方法。

回傳寫入緩衝區位元組的數目。

sock 必須是非阻塞 socket。

在 3.7 版被加入.

asyncloop.sock_recvfrom(sock,bufsize)

sock 接收最多bufsize 大小的資料單元。socket.recvfrom() 的非同步版本。

回傳一個元組 (received data, remote address)。

sock 必須是非阻塞 socket。

在 3.11 版被加入.

asyncloop.sock_recvfrom_into(sock,buf,nbytes=0)

sock 接收最多nbytes 大小的資料單元到bufsocket.recvfrom_into() 的非同步版本。

回傳一個元組 (number of bytes received, remote address)。

sock 必須是非阻塞 socket。

在 3.11 版被加入.

asyncloop.sock_sendall(sock,data)

data 發送到sock socket。socket.sendall() 的非同步版本。

此方法將繼續發送到 socket,直到data 中的所有資料都已發送或發生錯誤。成功時回傳None。錯誤時引發例外。此外,沒有辦法確定接收端成功處理了多少資料(如果有的話)。

sock 必須是非阻塞 socket。

在 3.7 版的變更:儘管該方法一直被記錄為協程方法,但在 Python 3.7 之前它回傳Future。從 Python 3.7 開始,這是一個asyncdef 方法。

asyncloop.sock_sendto(sock,data,address)

sockaddress 發送一個資料單元。socket.sendto() 的非同步版本。

回傳發送的位元組數。

sock 必須是非阻塞 socket。

在 3.11 版被加入.

asyncloop.sock_connect(sock,address)

sock 連線到位於address 的遠端 socket。

socket.connect() 的非同步版本。

sock 必須是非阻塞 socket。

在 3.5.2 版的變更:不再需要解析addresssock_connect 將嘗試透過呼叫socket.inet_pton() 檢查address 是否已解析。如果沒有,將使用loop.getaddrinfo() 解析address

asyncloop.sock_accept(sock)

接受一個連線。模擬阻塞的socket.accept() 方法。

Socket 必須繫結到一個地址並偵聽連線。回傳值是一個(conn,address) 對,其中conn 是一個 socket 物件,可在連線上發送和接收資料,address 是連接另一端對應的 socket 地址。

sock 必須是非阻塞 socket。

在 3.7 版的變更:儘管該方法一直被記錄為協程方法,但在 Python 3.7 之前它回傳Future。從 Python 3.7 開始,這是一個asyncdef 方法。

asyncloop.sock_sendfile(sock,file,offset=0,count=None,*,fallback=True)

如果可行,使用高性能os.sendfile 發送檔案。回傳發送的總位元組數。

socket.sendfile() 的非同步版本。

sock 必須是非阻塞的socket.SOCK_STREAMsocket

file 必須是以二進位模式打開的常規檔案物件。

offset 告訴從哪裡開始讀取檔案。如果指定了,count 是要傳輸的總位元組數,而不是發送檔案直到達到 EOF。即使此方法引發錯誤時,檔案位置也始終更新,可以使用file.tell() 取得實際發送的位元組數。

當設置為True 時,fallback 使 asyncio 在平台不支援 sendfile 系統呼叫時(例如 Windows 或 Unix 上的 SSL socket)手動讀取和發送檔案。

如果系統不支援sendfile 系統呼叫且fallbackFalse,引發SendfileNotAvailableError

sock 必須是非阻塞 socket。

在 3.7 版被加入.

DNS

asyncloop.getaddrinfo(host,port,*,family=0,type=0,proto=0,flags=0)

socket.getaddrinfo() 的非同步版本。

asyncloop.getnameinfo(sockaddr,flags=0)

socket.getnameinfo() 的非同步版本。

備註

Bothgetaddrinfo andgetnameinfo internally utilize their synchronousversions through the loop's default thread pool executor.When this executor is saturated, these methods may experience delays,which higher-level networking libraries may report as increased timeouts.To mitigate this, consider using a custom executor for other user tasks,or setting a default executor with a larger number of workers.

在 3.7 版的變更:getaddrinfogetnameinfo 方法一直被記錄為回傳協程,但在 Python 3.7 之前它們實際上回傳asyncio.Future 物件。從 Python 3.7 開始,兩個方法都是協程。

使用管道

asyncloop.connect_read_pipe(protocol_factory,pipe)

在事件迴圈中註冊pipe 的讀取端。

protocol_factory 必須是一個回傳asyncio protocol 實作的可呼叫函式。

pipe類檔案物件

回傳(transport,protocol) 對,其中transport 支援ReadTransport 介面,protocol 是由protocol_factory 實例化的物件。

使用SelectorEventLoop 事件迴圈時,pipe 設置為非阻塞模式。

asyncloop.connect_write_pipe(protocol_factory,pipe)

在事件迴圈中註冊pipe 的寫入端。

protocol_factory 必須是一個回傳asyncio protocol 實作的可呼叫函式。

pipefile-like object

回傳(transport,protocol) 對,其中transport 支援WriteTransport 介面,protocol 是由protocol_factory 實例化的物件。

使用SelectorEventLoop 事件迴圈時,pipe 設置為非阻塞模式。

備註

SelectorEventLoop 在 Windows 上不支援上述方法。對於 Windows 請使用ProactorEventLoop

Unix 訊號

loop.add_signal_handler(signum,callback,*args)

callback 設置為signum 訊號的處理程式。

該回呼將由loop 叫用,與該事件迴圈的其他排隊回呼和可運行的協程一起。與使用signal.signal() 註冊的訊號處理程式不同,使用此函式註冊的回呼允許與事件迴圈進行互動。

如果訊號號無效或不可捕獲,引發ValueError。如果設定處理程序有問題,拋出RuntimeError

使用functools.partial()callback傳送關鍵字引數

signal.signal() 一樣,此函式必須在主執行緒中叫用。

loop.remove_signal_handler(sig)

移除sig 訊號的處理程式。

如果訊號處理程式被移除,回傳True;如果給定訊號沒有設置處理程式,回傳False

適用: Unix.

也參考

signal 模組。

在執行緒池或行程池中執行程式碼

awaitableloop.run_in_executor(executor,func,*args)

安排在指定的執行器中呼叫func

Theexecutor argument should be anconcurrent.futures.Executorinstance. The default executor is used ifexecutor isNone.The default executor can be set byloop.set_default_executor(),otherwise, aconcurrent.futures.ThreadPoolExecutor will belazy-initialized and used byrun_in_executor() if needed.

範例:

importasyncioimportconcurrent.futuresdefblocking_io():# File operations (such as logging) can block the# event loop: run them in a thread pool.withopen('/dev/urandom','rb')asf:returnf.read(100)defcpu_bound():# CPU-bound operations will block the event loop:# in general it is preferable to run them in a# process pool.returnsum(i*iforiinrange(10**7))asyncdefmain():loop=asyncio.get_running_loop()## Options:# 1. Run in the default loop's executor:result=awaitloop.run_in_executor(None,blocking_io)print('default thread pool',result)# 2. Run in a custom thread pool:withconcurrent.futures.ThreadPoolExecutor()aspool:result=awaitloop.run_in_executor(pool,blocking_io)print('custom thread pool',result)# 3. Run in a custom process pool:withconcurrent.futures.ProcessPoolExecutor()aspool:result=awaitloop.run_in_executor(pool,cpu_bound)print('custom process pool',result)if__name__=='__main__':asyncio.run(main())

請注意,由於multiprocessing(由ProcessPoolExecutor 使用)的特殊性,選項 3 需要進入點保護(if__name__=='__main__')。請參閱主模組的安全引入

此方法回傳asyncio.Future 物件。

使用functools.partial() 將來關鍵字引數傳遞func

在 3.5.3 版的變更:loop.run_in_executor() 不再配置它建立的執行緒池執行器的max_workers,而是讓執行緒池執行器(ThreadPoolExecutor)設定預設值。

loop.set_default_executor(executor)

executor 設置為run_in_executor() 使用的預設執行器。executor 必須是ThreadPoolExecutor 的實例。

在 3.11 版的變更:executor 必須是ThreadPoolExecutor 的實例。

錯誤處理 API

允許自定義事件迴圈中的例外處理方式。

loop.set_exception_handler(handler)

handler 設定為新的事件迴圈例外處理程式。

如果handlerNone,則將設置預設例外處理程式。否則,handler 必須是一個可呼叫物件,簽名匹配(loop,context),其中loop 是參照活躍事件迴圈的,context 是包含例外詳細資訊的dict 物件(有關情境的詳細資訊,請參閱call_exception_handler() 文件)。

如果代表TaskHandle 呼叫處理程式,它將在該任務或回呼處理程式的contextvars.Context 中運行。

在 3.12 版的變更:處理程式可能在引發例外的任務或處理程式的Context 中被呼叫。

loop.get_exception_handler()

回傳目前的例外處理程式,如果未設置自定義例外處理程式,則回傳None

在 3.5.2 版被加入.

loop.default_exception_handler(context)

預設例外處理程式。

當發生例外且未設置例外處理程式時呼叫此函式。自定義例外處理程式可以呼叫此函式以轉由預設處理程式處理。

context 參數與call_exception_handler() 中的意思相同。

loop.call_exception_handler(context)

呼叫目前事件迴圈例外處理程式。

context 是一個包含以下鍵的dict 物件(未來的 Python 版本中可能會引入新的鍵):

  • 'message':錯誤訊息;

  • 'exception'(可選):例外物件;

  • 'future'(可選):asyncio.Future 實例;

  • 'task'(可選):asyncio.Task 實例;

  • 'handle'(可選):asyncio.Handle 實例;

  • 'protocol'(可選):Protocol 實例;

  • 'transport'(可選):Transport 實例;

  • 'socket'(可選):socket.socket 實例;

  • 'source_traceback' (optional): Traceback of the source;

  • 'handle_traceback' (optional): Traceback of the handle;

  • 'asyncgen'(可選): 非同步產生器引發

    例外。

備註

此方法不應在子類別事件迴圈中被覆寫。為了自定義例外處理,請使用set_exception_handler() 方法。

啟用除錯模式

loop.get_debug()

取得事件迴圈的除錯模式(bool)。

如果環境變數PYTHONASYNCIODEBUG 被設定為非空字串,則預設值為True,否則為False

loop.set_debug(enabled:bool)

設定事件迴圈的除錯模式。

在 3.7 版的變更:現在也可以使用新的Python 開發模式 啟用除錯模式。

loop.slow_callback_duration

此屬性可用於設定被視為"慢"的最短執行時間(以秒為單位)。啟用偵錯模式後,"慢"回呼將被記錄。

預設值為 100 毫秒

運行子行程

本小節描述的方法是低階的。在常規的 async/await 程式碼中,請考慮使用高階asyncio.create_subprocess_shell()asyncio.create_subprocess_exec() 輔助功能而不是。

備註

在 Windows 上,預設事件迴圈ProactorEventLoop 支援子行程,而SelectorEventLoop 不支援。詳細資訊請參見Windows 上對於子行程的支援

asyncloop.subprocess_exec(protocol_factory,*args,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,**kwargs)

args 指定的一個或多個字串引數建立子行程。

args 必須是由以下項表示的字串串列:

第一個字串指定程序可執行檔案,其餘字串指定引數。字串引數一起組成程序的argv

這與標準函式庫subprocess.Popen 類似,使用shell=False 呼叫並將字串串列作為第一個引數傳遞;然而,Popen 接受單個字串串列引數,subprocess_exec 接受多個字串引數。

protocol_factory 必須是回傳asyncio.SubprocessProtocol 子類別的可呼叫物件。

其他參數:

  • stdin 可以是以下任意一個:

    • 類檔案物件

    • 現有的檔案描述器(正整數),例如用os.pipe() 建立的

    • subprocess.PIPE 常數(預設),它將建立一個新的管道並連線,

    • None 將使子行程從此行程繼承檔案描述器

    • subprocess.DEVNULL 常數,表示將使用特殊的os.devnull 檔案

  • stdout 可以是以下任意一個:

    • 類檔案物件

    • subprocess.PIPE 常數(預設),它將建立一個新的管道並連線,

    • None 將使子行程從此行程繼承檔案描述器

    • subprocess.DEVNULL 常數,表示將使用特殊的os.devnull 檔案

  • stderr 可以是以下任意一個:

    • 類檔案物件

    • subprocess.PIPE 常數(預設),它將建立一個新的管道並連線,

    • None 將使子行程從此行程繼承檔案描述器

    • subprocess.DEVNULL 常數,表示將使用特殊的os.devnull 檔案

    • subprocess.STDOUT 常數,它將標準錯誤串流連線到行程的標準輸出串流

  • 所有其他關鍵字引數都會傳遞給subprocess.Popen 而不進行直譯,但bufsizeuniversal_newlinesshelltextencodingerrors 除外,這些不應該指定。

    asyncio 子行程 API 不支援將串流解碼為文本。可以使用bytes.decode() 將從串流回傳的位元組轉換為文本。

如果傳遞給stdinstdoutstderr 的類檔案物件表示管道,則該管道的另一端應該使用connect_write_pipe()connect_read_pipe() 註冊到事件迴圈中。

有關其他引數的文件,請參閱subprocess.Popen 類別的建構函式。

回傳(transport,protocol) 對,其中transport 符合asyncio.SubprocessTransport 基底類別,protocol 是由protocol_factory 實例化的物件。

asyncloop.subprocess_shell(protocol_factory,cmd,*,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,**kwargs)

使用平台的 "shell" 語法從cmd 建立子行程,cmd 可以是str 或編碼為檔案系統編碼bytes 字串。

這類似於標準函式庫中的subprocess.Popen 類別,使用shell=True 呼叫。

protocol_factory 必須是回傳SubprocessProtocol 子類別的可呼叫物件。

有關其餘引數的更多詳細資訊,請參閱subprocess_exec()

回傳一對(transport,protocol),其中transport 符合SubprocessTransport 基底類別,而protocol 是由protocol_factory 實例化的物件。

備註

由應用程式負責確保適當引用所有空白和特殊字元,以避免shell 注入風險。可以使用shlex.quote() 函式來正確跳脫用於構建 shell 命令的字串中的空白和特殊字元。

回呼處理

classasyncio.Handle

loop.call_soon()loop.call_soon_threadsafe() 回傳的回呼包裝器。

get_context()

回傳與處理相關聯的contextvars.Context 物件。

在 3.12 版被加入.

cancel()

取消回呼。如果回呼已被取消或執行,此方法將不起作用。

cancelled()

如果回呼已被取消,回傳True

在 3.7 版被加入.

classasyncio.TimerHandle

loop.call_later()loop.call_at() 回傳的回呼包裝器。

這個類別是Handle 的子類別。

when()

回傳預定的回呼時間,以float 秒為單位。

時間是一個絕對的時間戳,使用與loop.time() 相同的時間參照。

在 3.7 版被加入.

Server 物件

Server 物件是由loop.create_server()loop.create_unix_server()start_server()start_unix_server() 函式所建立。

請勿直接實例化Server 類別。

classasyncio.Server

Server 物件是非同步情境管理器。當在asyncwith 陳述中使用時,可以保證在完成asyncwith 陳述時,Server 物件將會關閉並停止接受新的連線:

srv=awaitloop.create_server(...)asyncwithsrv:# 一些程式碼# 此時 srv 已關閉,不再接受新的連線。

在 3.7 版的變更:自 Python 3.7 起,Server 物件是非同步情境管理器。

在 3.11 版的變更:此類別在 Python 3.9.11、3.10.3 和 3.11 中以asyncio.Server 的形式被公開。

close()

停止服務:關閉監聽的 sockets 並將sockets 屬性設為None

代表現有傳入用戶端連線的 sockets 仍然保持開啟。

伺服器以非同步方式關閉;使用wait_close() 協程等待伺服器關閉(不再有活躍連線)。

close_clients()

關閉所有現有的傳入客戶端連線。

在所有關聯的傳輸上呼叫close()

close() should be called beforeclose_clients() whenclosing the server to avoid races with new clients connecting.

在 3.13 版被加入.

abort_clients()

立即關閉所有現有的傳入客戶端連線,而不等待待定操作完成。

在所有關聯的傳輸上呼叫close()

close() should be called beforeabort_clients() whenclosing the server to avoid races with new clients connecting.

在 3.13 版被加入.

get_loop()

回傳與伺服器物件關聯的事件迴圈。

在 3.7 版被加入.

asyncstart_serving()

開始接受連線。

此方法是幂等的,因此可以在伺服器已經運行時呼叫。

start_serving 僅限關鍵字參數只能在loop.create_server()asyncio.start_server() 中使用,允許建立一個最初不接受連線的 Server 物件。在這種情況下,可以使用Server.start_serving()Server.serve_forever() 來使 Server 開始接受連線。

在 3.7 版被加入.

asyncserve_forever()

開始接受連線,直到協程被取消。取消serve_forever 任務會導致伺服器關閉。

如果伺服器已經接受連線,則可以呼叫此方法。每個Server 物件只能存在一個serve_forever 任務。

範例:

asyncdefclient_connected(reader,writer):# 透過讀取器/寫入器串流# 與客戶端溝通。例如:awaitreader.readline()asyncdefmain(host,port):srv=awaitasyncio.start_server(client_connected,host,port)awaitsrv.serve_forever()asyncio.run(main('127.0.0.1',0))

在 3.7 版被加入.

is_serving()

如果伺服器正在接受新連線,則回傳True

在 3.7 版被加入.

asyncwait_closed()

等待close() 方法完成且所有活動連線都已結束。

sockets

伺服器正在監聽的類似 socket 的物件串列,asyncio.trsock.TransportSocket

在 3.7 版的變更:在 Python 3.7 之前,Server.sockets 曾經直接回傳內部伺服器 sockets 的串列。在 3.7 中回傳了該串列的副本。

事件迴圈實作

asyncio 內附兩個不同的事件迴圈實作:SelectorEventLoopProactorEventLoop

預設情況下,asyncio 被配置為要使用EventLoop

classasyncio.SelectorEventLoop

基於selectors 模組的一個AbstractEventLoop 子類別。

使用特定平台上最有效的selector。也可以手動配置要使用的確切 selector 實作:

importasyncioimportselectorsclassMyPolicy(asyncio.DefaultEventLoopPolicy):defnew_event_loop(self):selector=selectors.SelectSelector()returnasyncio.SelectorEventLoop(selector)asyncio.set_event_loop_policy(MyPolicy())

適用: Unix, Windows.

classasyncio.ProactorEventLoop

用於 Windows 的AbstractEventLoop 子類別,使用「I/O 完成埠 (IOCP, I/O Completion Ports)」。

適用: Windows.

classasyncio.EventLoop

An alias to the most efficient available subclass ofAbstractEventLoop for the givenplatform.

在 Unix 上是SelectorEventLoop 的別名,在 Windows 上是ProactorEventLoop 的別名。

在 3.13 版被加入.

classasyncio.AbstractEventLoop

為符合 asyncio 標準的事件迴圈的抽象基礎類別。

事件迴圈方法 部分列出了替代AbstractEventLoop 實作應該定義的所有方法。

範例

請注意,本節中的所有範例都故意 展示如何使用低階事件迴圈 API,如loop.run_forever()loop.call_soon()。現代 asyncio 應用程式很少需要這種方式撰寫;請考慮使用高階的函式,如asyncio.run()

使用 call_soon() 的 Hello World 範例

使用loop.call_soon() 方法排程回呼的範例。回呼會顯示"HelloWorld",然後停止事件迴圈:

importasynciodefhello_world(loop):"""列印 'Hello World' 並停止事件迴圈的回呼"""print('Hello World')loop.stop()loop=asyncio.new_event_loop()# 排程對 hello_world() 的呼叫loop.call_soon(hello_world,loop)# 阻塞呼叫被 loop.stop() 中斷try:loop.run_forever()finally:loop.close()

也參考

使用協程和run() 函式建立的類似Hello World 範例。

使用 call_later() 顯示目前日期

一個回呼的範例,每秒顯示目前日期。回呼使用loop.call_later() 方法在 5 秒後重新排程自己,然後停止事件迴圈:

importasyncioimportdatetimedefdisplay_date(end_time,loop):print(datetime.datetime.now())if(loop.time()+1.0)<end_time:loop.call_later(1,display_date,end_time,loop)else:loop.stop()loop=asyncio.new_event_loop()# 排程 display_date() 的第一次呼叫end_time=loop.time()+5.0loop.call_soon(display_date,end_time,loop)# 阻塞呼叫被 loop.stop() 中斷try:loop.run_forever()finally:loop.close()

也參考

使用協程和run() 函式建立的類似current date 範例。

監聽檔案描述器以進行讀取事件

使用loop.add_reader() 方法等待檔案描述器接收到某些資料,然後關閉事件迴圈:

importasynciofromsocketimportsocketpair# 建立一對連接的檔案描述器rsock,wsock=socketpair()loop=asyncio.new_event_loop()defreader():data=rsock.recv(100)print("Received:",data.decode())# 我們完成了:註銷檔案描述器loop.remove_reader(rsock)# 停止事件迴圈loop.stop()# 為讀取事件註冊檔案描述器loop.add_reader(rsock,reader)# 模擬從網路接收資料loop.call_soon(wsock.send,'abc'.encode())try:# 運行事件迴圈loop.run_forever()finally:# 我們完成了。關閉 socket 和事件迴圈。rsock.close()wsock.close()loop.close()

也參考

設定 SIGINT 和 SIGTERM 的訊號處理程式

(此signals 範例僅在 Unix 上運作。)

使用loop.add_signal_handler() 方法註冊訊號SIGINTSIGTERM 的處理程式:

importasyncioimportfunctoolsimportosimportsignaldefask_exit(signame,loop):print("got signal%s: exit"%signame)loop.stop()asyncdefmain():loop=asyncio.get_running_loop()forsignamein{'SIGINT','SIGTERM'}:loop.add_signal_handler(getattr(signal,signame),functools.partial(ask_exit,signame,loop))awaitasyncio.sleep(3600)print("Event loop running for 1 hour, press Ctrl+C to interrupt.")print(f"pid{os.getpid()}: send SIGINT or SIGTERM to exit.")asyncio.run(main())