From d92716193ebe1c1d91d5e9aaf729cf15032d71e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Wed, 4 Mar 2026 16:09:47 +0100 Subject: [PATCH 1/2] Fix test execution under Python 3.14 see the Python 3.14 changelog for the breaking change to multiprocessing fork on linux per default https://docs.python.org/3/whatsnew/3.14.html#:~:text=On%20Unix%20platforms%20other%20than%20macOS%2C%20%E2%80%98forkserver%E2%80%99%20is%20now%20the%20default%20start%20method%20for%20ProcessPoolExecutor%20(replacing%20%E2%80%98fork%E2%80%99).%20This%20change%20does%20not%20affect%20Windows%20or%20macOS%2C%20where%20%E2%80%98spawn%E2%80%99%20remains%20the%20default%20start%20method Multiple tests failed with similar errors like: _____________________________ test_peer_connection _____________________________ [gw0] linux -- Python 3.14.3 /nix/store/8gnchv834z56s561v3sx2h0ra1a2xn46-python3-3.14.3/bin/python3.14 test_server_sync = [] test_client_sync = event_loop = <_UnixSelectorEventLoop running=False closed=False debug=False> def test_peer_connection( test_server_sync: list[socket.socket], test_client_sync: socket.socket, event_loop: asyncio.AbstractEventLoop, ) -> None: """Run a full flow of with a peer.""" worker = ServerWorker(FERNET_TOKENS) valid = datetime.now(tz=UTC) + timedelta(days=1) aes_key = os.urandom(32) aes_iv = os.urandom(16) hostname = "localhost" fernet_token = create_peer_config(valid.timestamp(), hostname, aes_key, aes_iv) > worker.start() tests/server/test_worker.py:43: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /nix/store/8gnchv834z56s561v3sx2h0ra1a2xn46-python3-3.14.3/lib/python3.14/multiprocessing/process.py:121: in start self._popen = self._Popen(self) ^^^^^^^^^^^^^^^^^ /nix/store/8gnchv834z56s561v3sx2h0ra1a2xn46-python3-3.14.3/lib/python3.14/multiprocessing/context.py:224: in _Popen return _default_context.get_context().Process._Popen(process_obj) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /nix/store/8gnchv834z56s561v3sx2h0ra1a2xn46-python3-3.14.3/lib/python3.14/multiprocessing/context.py:300: in _Popen return Popen(process_obj) ^^^^^^^^^^^^^^^^^^ /nix/store/8gnchv834z56s561v3sx2h0ra1a2xn46-python3-3.14.3/lib/python3.14/multiprocessing/popen_forkserver.py:35: in __init__ super().__init__(process_obj) /nix/store/8gnchv834z56s561v3sx2h0ra1a2xn46-python3-3.14.3/lib/python3.14/multiprocessing/popen_fork.py:20: in __init__ self._launch(process_obj) /nix/store/8gnchv834z56s561v3sx2h0ra1a2xn46-python3-3.14.3/lib/python3.14/multiprocessing/popen_forkserver.py:47: in _launch reduction.dump(process_obj, buf) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ obj = file = <_io.BytesIO object at 0x7fffef185c10>, protocol = None def dump(obj, file, protocol=None): '''Replacement for pickle.dump() using ForkingPickler.''' > ForkingPickler(file, protocol).dump(obj) E TypeError: cannot pickle 'weakref.ReferenceType' object E when serializing dict item '_weakref' E when serializing multiprocessing.util.Finalize state E when serializing multiprocessing.util.Finalize object E when serializing dict item 'finalizer' E when serializing multiprocessing.popen_forkserver.Popen state E when serializing multiprocessing.popen_forkserver.Popen object E when serializing dict item '_popen' E when serializing multiprocessing.context.ForkServerProcess state E when serializing multiprocessing.context.ForkServerProcess object E when serializing dict item '_process' E when serializing multiprocessing.managers.SyncManager state E when serializing multiprocessing.managers.SyncManager object E when serializing dict item '_manager' E when serializing snitun.server.worker.ServerWorker state E when serializing snitun.server.worker.ServerWorker object /nix/store/8gnchv834z56s561v3sx2h0ra1a2xn46-python3-3.14.3/lib/python3.14/multiprocessing/reduction.py:60: TypeError ---------------------------- Captured stderr setup ----------------------------- DEBUG:asyncio:Using selector: EpollSelector ------------------------------ Captured log setup ------------------------------ DEBUG asyncio:selector_events.py:64 Using selector: EpollSelector --- tests/conftest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 78594e7e..1b9c184a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,9 +5,11 @@ from dataclasses import dataclass, field from datetime import UTC, datetime, timedelta import logging +import multiprocessing import os import select import socket +import sys from threading import Thread from unittest.mock import patch @@ -338,3 +340,6 @@ async def test_client_peer(peer_listener: PeerListener) -> AsyncGenerator[Client yield Client(reader, writer) writer.close() + +if sys.platform == "linux": + multiprocessing.set_start_method("fork", force=True) From b4275208d7aa596cae2ef2cd10dc5446712736f0 Mon Sep 17 00:00:00 2001 From: Sandro Date: Wed, 4 Mar 2026 23:53:00 +0100 Subject: [PATCH 2/2] Add Python 3.14 to CI --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab5a6843..789a3a80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,10 +28,10 @@ jobs: timeout-minutes: 6 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Setup Python 3.13 + - name: Setup Python 3.14 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: "3.13" + python-version: "3.14" cache: pip - name: Install dependencies shell: bash @@ -54,6 +54,7 @@ jobs: python-version: - "3.12" - "3.13" + - "3.14" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python ${{ matrix.python-version }}