From 46bad0b63910f864ceeab7f1affe32e8d81a2ea4 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Tue, 11 Aug 2026 01:10:18 +0800 Subject: [PATCH] fix: use socket.fromfd in send_batch_sync for Python 3.13 compat Python 3.13's asyncio TransportSocket wrapper rejects setblocking(). Dup the underlying fd via socket.fromfd() into a plain socket that the executor thread can drive in blocking mode, then detach() after the batch to avoid closing the transport's fd. --- gns3server/compute/ubridge/ubridge_hypervisor.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/gns3server/compute/ubridge/ubridge_hypervisor.py b/gns3server/compute/ubridge/ubridge_hypervisor.py index 2cbc3a7eb..009416d52 100644 --- a/gns3server/compute/ubridge/ubridge_hypervisor.py +++ b/gns3server/compute/ubridge/ubridge_hypervisor.py @@ -20,6 +20,7 @@ import logging import asyncio import threading import concurrent.futures +import socket as _socket from gns3server.utils.asyncio import locking from .ubridge_error import UbridgeError @@ -296,10 +297,15 @@ class UBridgeHypervisor: transport = self._writer.transport if transport is None or transport.is_closing(): raise UbridgeError("Transport closed") - sock = transport.get_extra_info("socket") - if sock is None: + tr_sock = transport.get_extra_info("socket") + if tr_sock is None: raise UbridgeError("No underlying socket for sync send_batch") + # Python 3.13's transport socket wrapper (trsock) rejects + # setblocking(), so dup the underlying fd into a plain socket + # that the executor thread can drive in blocking mode. + sock = _socket.fromfd(tr_sock.fileno(), tr_sock.family, _socket.SOCK_STREAM) + # Serialise access to this hypervisor's socket — only one batch (sync # or async) talks to uBridge at a time. The node-level async lock # (:func:`_ubridge_send`) is held for the entire executor call, so no @@ -343,3 +349,4 @@ class UBridgeHypervisor: self._recv_buf = b"" finally: sock.setblocking(False) + sock.detach() # release the dup'd fd, don't close transport