From 3398479138153c84730ec26ed5202389ea050c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Saugat=20Pachhai=20=28=E0=A4=B8=E0=A5=8C=E0=A4=97=E0=A4=BE?= =?UTF-8?q?=E0=A4=A4=29?= Date: Thu, 30 Oct 2025 10:43:39 +0545 Subject: [PATCH 1/4] try to fix Python getting stuck during finalizer See #63 for more details. --- sshfs/spec.py | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/sshfs/spec.py b/sshfs/spec.py index 88f7f2b..5c1fc73 100644 --- a/sshfs/spec.py +++ b/sshfs/spec.py @@ -8,7 +8,13 @@ import asyncssh from asyncssh.sftp import SFTPOpUnsupported -from fsspec.asyn import AsyncFileSystem, async_methods, sync, sync_wrapper +from fsspec.asyn import ( + AsyncFileSystem, + FSTimeoutError, + async_methods, + sync, + sync_wrapper, +) from fsspec.utils import infer_storage_options from sshfs.file import SSHFile @@ -71,7 +77,7 @@ def __init__( **_client_args, ) weakref.finalize( - self, sync, self.loop, self._finalize, self._pool, self._stack + self, self._finalize, self.loop, self._pool, self._stack ) @classmethod @@ -101,15 +107,29 @@ async def _connect( connect = sync_wrapper(_connect) @staticmethod - async def _finalize(pool, stack): - await pool.close() - - # If an error occurs while the SSHFile is trying to - # open the native file, then the client might get broken - # due to partial initialization. We are just going to ignore - # the errors that arises on the finalization layer - with suppress(BrokenPipeError): - await stack.aclose() + def _finalize(loop, pool, stack): + async def close(): + await pool.close() + # If an error occurs while the SSHFile is trying to + # open the native file, then the client might get broken + # due to partial initialization. We are just going to ignore + # the errors that arises on the finalization layer + with suppress(BrokenPipeError): + await stack.aclose() + + if loop is not None and loop.is_running(): + try: + loop = asyncio.get_running_loop() + loop.create_task(close()) + return + except RuntimeError: + pass + + try: + sync(loop, close, timeout=0.1) + return + except FSTimeoutError: + pass @property def client(self): From 7abb5dd701768482949249ed0072f4b549b024f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Saugat=20Pachhai=20=28=E0=A4=B8=E0=A5=8C=E0=A4=97=E0=A4=BE?= =?UTF-8?q?=E0=A4=A4=29?= Date: Thu, 30 Oct 2025 14:25:35 +0545 Subject: [PATCH 2/4] test --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f8f78f2..e118918 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,11 +15,11 @@ jobs: test: runs-on: ${{ matrix.os }} strategy: - fail-fast: true + fail-fast: false matrix: - os: [ubuntu-latest, macos-latest] - pyv: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + os: [ubuntu-latest] + pyv: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 @@ -34,6 +34,7 @@ jobs: python -m pip install -e . - name: Test + timeout-minutes: 1 run: python -m pytest --cov-report=xml --cov=. - name: Upload coverage to Codecov From 276f7bdc805e29ef55a6df29d76d23eced3cc1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Saugat=20Pachhai=20=28=E0=A4=B8=E0=A5=8C=E0=A4=97=E0=A4=BE?= =?UTF-8?q?=E0=A4=A4=29?= Date: Fri, 5 Dec 2025 10:46:04 +0545 Subject: [PATCH 3/4] use fork of mockssh-server --- .github/workflows/test.yml | 2 +- requirements-dev.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e118918..bf1c749 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: matrix: os: [ubuntu-latest] - pyv: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14","3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + pyv: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 diff --git a/requirements-dev.txt b/requirements-dev.txt index 1b4add2..844661d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pytest pytest-asyncio pytest-cov -mock-ssh-server +mock-ssh-server @ git+https://github.com/skshetry/mock-ssh-server.git@command-queue-race importlib-metadata >= 6.0.0 From b1bc600a930735b65cdfa78a0b68d661b084a36f Mon Sep 17 00:00:00 2001 From: skshetry <18718008+skshetry@users.noreply.github.com> Date: Fri, 5 Dec 2025 10:47:25 +0545 Subject: [PATCH 4/4] Apply suggestions from code review --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf1c749..8e67ed2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,10 +15,10 @@ jobs: test: runs-on: ${{ matrix.os }} strategy: - fail-fast: false + fail-fast: true matrix: - os: [ubuntu-latest] + os: [ubuntu-latest, macos-latest] pyv: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: