From abb860eeb1788616bae4cb7ecea6fef791b620dd Mon Sep 17 00:00:00 2001 From: Marcus Gelderie Date: Thu, 4 Jun 2026 13:34:49 +0200 Subject: [PATCH 1/2] capture.py: Ensure compatibility with Python >= 3.14. Most recent Python versions removed parts of the API used in capture.py. This patch restores compatibility. For more recent Python versions, this patch removes usage of the removed ChildWatcher APIs (and of the deprecated Policies) and simply retrieves or creates the event loop in the recommended way. As far as I can tell, there is not gap in functionality: Using a SafeChildWatcher instead of ThreadedChildWatcher on Unix systems suppressed an error message about already awaited children, but did not resolve the underlying issue (calling wait on the same PID multiple times), which is moreover an uncritical condition. In most recent versions of the Python asyncio APIs, it is not longer possible to change the ChildWatcher in this fashion anyways, and API version >= 3.14 no longer has a SafeChildWatcher, even internally. --- src/pyshark/capture/capture.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pyshark/capture/capture.py b/src/pyshark/capture/capture.py index b7aef6ef..cfd25afb 100644 --- a/src/pyshark/capture/capture.py +++ b/src/pyshark/capture/capture.py @@ -168,6 +168,14 @@ def _verify_capture_parameters(self): def _setup_eventloop(self): """Sets up a new eventloop as the current one according to the OS.""" + if sys.version_info >= (3, 14): + try: + self.eventloop = asyncio.get_event_loop() + except RuntimeError: + self.eventloop = asyncio.new_event_loop() + asyncio.set_event_loop(self.eventloop) + return + if os.name == "nt": current_eventloop = asyncio.get_event_loop_policy().get_event_loop() if isinstance(current_eventloop, asyncio.ProactorEventLoop): From ad4602305b349d429cfd326317b7bd970fdc0977 Mon Sep 17 00:00:00 2001 From: Marcus Gelderie Date: Thu, 4 Jun 2026 14:45:38 +0200 Subject: [PATCH 2/2] tests: Fix pickling issue in tests. One test uses multiprocessing and fails due to a pickling error. This patch moves the creation of the capture instance into the new process thereby eliminating the need for pickling a capture object. --- tests/test_cap_operations.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/test_cap_operations.py b/tests/test_cap_operations.py index a6670f1f..bbe34952 100644 --- a/tests/test_cap_operations.py +++ b/tests/test_cap_operations.py @@ -7,7 +7,9 @@ import pytest +import pyshark from pyshark.packet.packet_summary import PacketSummary +from tests.conftest import example_pcap_path def test_packet_callback_called_for_each_packet(lazy_simple_capture): @@ -60,17 +62,18 @@ def test_getting_packet_summary(simple_summary_capture): assert simple_summary_capture[0]._fields -def _iterate_capture_object(cap_obj, q): +def _iterate_capture_object(example_pcap_path, q): + cap_obj = pyshark.FileCapture(example_pcap_path, debug=True, only_summaries=True) + cap_obj.display_filter = "frame.len == 1" for _ in cap_obj: pass q.put(True) -def test_iterate_empty_psml_capture(simple_summary_capture): - simple_summary_capture.display_filter = "frame.len == 1" +def test_iterate_empty_psml_capture(example_pcap_path): q = Queue() p = Process(target=_iterate_capture_object, - args=(simple_summary_capture, q)) + args=(example_pcap_path, q)) p.start() p.join(2) try: