From 9eae60d91343d4df8d87ef7ce578d3aa84c4d08c Mon Sep 17 00:00:00 2001 From: pranaysb Date: Wed, 22 Jul 2026 19:36:52 +0530 Subject: [PATCH 01/11] Fix NoneType error on os.fork after GC for processors Assisted-by: Claude Opus 4.6 --- .../sdk/_shared_internal/__init__.py | 8 ++++- .../sdk/metrics/_internal/export/__init__.py | 9 +++-- .../test_periodic_exporting_metric_reader.py | 36 +++++++++++++++++++ .../shared_internal/test_batch_processor.py | 35 ++++++++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py index 3e2b8a263a4..fdfe373bea7 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py @@ -116,7 +116,13 @@ def __init__( self._worker_thread.start() if hasattr(os, "register_at_fork"): weak_reinit = weakref.WeakMethod(self._at_fork_reinit) - os.register_at_fork(after_in_child=lambda: weak_reinit()()) # pyright: ignore[reportOptionalCall] pylint: disable=unnecessary-lambda + + def _after_in_child() -> None: + reinit = weak_reinit() + if reinit is not None: + reinit() + + os.register_at_fork(after_in_child=_after_in_child) self._pid = os.getpid() metrics.register_queue_size(lambda: len(self._queue)) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py index aee356f78d2..de73538aff1 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py @@ -531,9 +531,12 @@ def __init__( if hasattr(os, "register_at_fork"): weak_at_fork = weakref.WeakMethod(self._at_fork_reinit) - os.register_at_fork( - after_in_child=lambda: weak_at_fork()() # pylint: disable=unnecessary-lambda - ) + def _after_in_child() -> None: + at_fork = weak_at_fork() + if at_fork is not None: + at_fork() + + os.register_at_fork(after_in_child=_after_in_child) elif self._export_interval_millis <= 0: raise ValueError( f"interval value {self._export_interval_millis} is invalid \ diff --git a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py index ab8877c21c3..381881c9b24 100644 --- a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py +++ b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py @@ -5,6 +5,7 @@ import gc import math +import os import weakref from logging import WARNING from time import sleep, time_ns @@ -307,6 +308,41 @@ def test_metric_exporer_gc(self): "The PeriodicExportingMetricReader object created by this test wasn't garbage collected", ) + @pytest.mark.skipif(not hasattr(os, "fork"), reason="needs *nix") + def test_garbage_collected_processor_does_not_crash_on_fork(self): + import os + import sys + + exporter = FakeMetricsExporter( + preferred_aggregation={ + Counter: LastValueAggregation(), + }, + ) + processor = PeriodicExportingMetricReader(exporter) + processor.shutdown() + del processor + gc.collect() + + # The bug causes an unraisable exception to be printed to stderr. + # We redirect stderr to a pipe before fork to capture it. + r_fd, w_fd = os.pipe() + + pid = os.fork() + if pid == 0: + os.close(r_fd) + os.dup2(w_fd, sys.stderr.fileno()) + # os.fork() has already run the at_fork hooks. + os._exit(0) + + os.close(w_fd) + _, status = os.waitpid(pid, 0) + self.assertEqual(status, 0) + + child_stderr = os.read(r_fd, 1024).decode("utf-8") + os.close(r_fd) + + self.assertNotIn("TypeError", child_stderr) + @patch.dict( "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"} ) diff --git a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py index 02337757073..41ef19f5851 100644 --- a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py +++ b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py @@ -215,6 +215,41 @@ def test_record_processor_is_garbage_collected( # Then the reference to the processor should no longer exist assert weak_ref() is None + @unittest.skipUnless( + hasattr(os, "fork"), + "needs *nix", + ) + def test_garbage_collected_processor_does_not_crash_on_fork( + self, batch_processor_class, telemetry + ): + import sys + + exporter = Mock() + processor = batch_processor_class(exporter) + processor.shutdown() + del processor + gc.collect() + + # The bug causes an unraisable exception to be printed to stderr. + # We redirect stderr to a pipe before fork to capture it. + r_fd, w_fd = os.pipe() + + pid = os.fork() + if pid == 0: + os.close(r_fd) + os.dup2(w_fd, sys.stderr.fileno()) + # os.fork() has already run the at_fork hooks. + os._exit(0) + + os.close(w_fd) + _, status = os.waitpid(pid, 0) + assert status == 0 + + child_stderr = os.read(r_fd, 1024).decode("utf-8") + os.close(r_fd) + + assert "TypeError" not in child_stderr + def test_shutdown_allows_1_export_to_finish( self, batch_processor_class, telemetry ): From 911346ae8b647ebd51330524e5f9b56d054d3819 Mon Sep 17 00:00:00 2001 From: pranaysb Date: Wed, 22 Jul 2026 22:39:45 +0530 Subject: [PATCH 02/11] Fix regression tests to correctly capture unraisablehook output in child process --- .../test_periodic_exporting_metric_reader.py | 30 +++++++++++++++---- .../shared_internal/test_batch_processor.py | 29 +++++++++++++++--- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py index 381881c9b24..7c13dad2593 100644 --- a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py +++ b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py @@ -6,6 +6,7 @@ import gc import math import os +import sys import weakref from logging import WARNING from time import sleep, time_ns @@ -310,9 +311,6 @@ def test_metric_exporer_gc(self): @pytest.mark.skipif(not hasattr(os, "fork"), reason="needs *nix") def test_garbage_collected_processor_does_not_crash_on_fork(self): - import os - import sys - exporter = FakeMetricsExporter( preferred_aggregation={ Counter: LastValueAggregation(), @@ -327,18 +325,40 @@ def test_garbage_collected_processor_does_not_crash_on_fork(self): # We redirect stderr to a pipe before fork to capture it. r_fd, w_fd = os.pipe() + # Save original stderr and redirect it to the pipe + # We also temporarily restore the default sys.unraisablehook. Pytest overrides + # it to capture exceptions in memory, which would be lost on os._exit(0). + old_fd = os.dup(sys.stderr.fileno()) + os.dup2(w_fd, sys.stderr.fileno()) + old_hook = sys.unraisablehook + sys.unraisablehook = sys.__unraisablehook__ + pid = os.fork() if pid == 0: os.close(r_fd) - os.dup2(w_fd, sys.stderr.fileno()) # os.fork() has already run the at_fork hooks. os._exit(0) + # Restore original stderr and hook in parent + sys.unraisablehook = old_hook + os.dup2(old_fd, sys.stderr.fileno()) + os.close(old_fd) os.close(w_fd) + _, status = os.waitpid(pid, 0) self.assertEqual(status, 0) - child_stderr = os.read(r_fd, 1024).decode("utf-8") + os.set_blocking(r_fd, False) + child_stderr = b"" + while True: + try: + chunk = os.read(r_fd, 4096) + if not chunk: + break + child_stderr += chunk + except BlockingIOError: + break + child_stderr = child_stderr.decode("utf-8") os.close(r_fd) self.assertNotIn("TypeError", child_stderr) diff --git a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py index 41ef19f5851..b719e9f66d0 100644 --- a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py +++ b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py @@ -6,6 +6,7 @@ import logging import multiprocessing import os +import sys import threading import time import unittest @@ -222,8 +223,6 @@ def test_record_processor_is_garbage_collected( def test_garbage_collected_processor_does_not_crash_on_fork( self, batch_processor_class, telemetry ): - import sys - exporter = Mock() processor = batch_processor_class(exporter) processor.shutdown() @@ -233,19 +232,41 @@ def test_garbage_collected_processor_does_not_crash_on_fork( # The bug causes an unraisable exception to be printed to stderr. # We redirect stderr to a pipe before fork to capture it. r_fd, w_fd = os.pipe() + + # Save original stderr and redirect it to the pipe + # We also temporarily restore the default sys.unraisablehook. Pytest overrides + # it to capture exceptions in memory, which would be lost on os._exit(0). + old_fd = os.dup(sys.stderr.fileno()) + os.dup2(w_fd, sys.stderr.fileno()) + old_hook = sys.unraisablehook + sys.unraisablehook = sys.__unraisablehook__ pid = os.fork() if pid == 0: os.close(r_fd) - os.dup2(w_fd, sys.stderr.fileno()) # os.fork() has already run the at_fork hooks. os._exit(0) + # Restore original stderr and hook in parent + sys.unraisablehook = old_hook + os.dup2(old_fd, sys.stderr.fileno()) + os.close(old_fd) os.close(w_fd) + _, status = os.waitpid(pid, 0) assert status == 0 - child_stderr = os.read(r_fd, 1024).decode("utf-8") + os.set_blocking(r_fd, False) + child_stderr = b"" + while True: + try: + chunk = os.read(r_fd, 4096) + if not chunk: + break + child_stderr += chunk + except BlockingIOError: + break + child_stderr = child_stderr.decode("utf-8") os.close(r_fd) assert "TypeError" not in child_stderr From ff3e4dd8416f8d5e6a3248ddf544d54af98696d9 Mon Sep 17 00:00:00 2001 From: pranaysb Date: Thu, 23 Jul 2026 07:08:55 +0530 Subject: [PATCH 03/11] docs: add changelog fragment for fork gc fix Assisted-by: Google Antigravity --- .changelog/5452.fixed | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/5452.fixed diff --git a/.changelog/5452.fixed b/.changelog/5452.fixed new file mode 100644 index 00000000000..be06e3e8263 --- /dev/null +++ b/.changelog/5452.fixed @@ -0,0 +1 @@ +`opentelemetry-sdk`: fix `TypeError` in `os.fork()` when a `BatchProcessor` or `PeriodicExportingMetricReader` is garbage collected From 24a7f809876beb6c72592689b93e2b4b618e0480 Mon Sep 17 00:00:00 2001 From: pranaysb Date: Thu, 23 Jul 2026 07:51:36 +0530 Subject: [PATCH 04/11] docs: rename changelog fragment to match PR number Assisted-by: Google Antigravity --- .changelog/{5452.fixed => 5453.fixed} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/{5452.fixed => 5453.fixed} (100%) diff --git a/.changelog/5452.fixed b/.changelog/5453.fixed similarity index 100% rename from .changelog/5452.fixed rename to .changelog/5453.fixed From 057cb3f73bd159bcf54e1328237a686e04302a39 Mon Sep 17 00:00:00 2001 From: pranaysb Date: Thu, 23 Jul 2026 07:55:42 +0530 Subject: [PATCH 05/11] style: fix trailing whitespace in tests Assisted-by: Google Antigravity --- .../tests/shared_internal/test_batch_processor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py index b719e9f66d0..891643fc813 100644 --- a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py +++ b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py @@ -232,7 +232,7 @@ def test_garbage_collected_processor_does_not_crash_on_fork( # The bug causes an unraisable exception to be printed to stderr. # We redirect stderr to a pipe before fork to capture it. r_fd, w_fd = os.pipe() - + # Save original stderr and redirect it to the pipe # We also temporarily restore the default sys.unraisablehook. Pytest overrides # it to capture exceptions in memory, which would be lost on os._exit(0). @@ -255,7 +255,7 @@ def test_garbage_collected_processor_does_not_crash_on_fork( _, status = os.waitpid(pid, 0) assert status == 0 - + os.set_blocking(r_fd, False) child_stderr = b"" while True: @@ -268,7 +268,7 @@ def test_garbage_collected_processor_does_not_crash_on_fork( break child_stderr = child_stderr.decode("utf-8") os.close(r_fd) - + assert "TypeError" not in child_stderr def test_shutdown_allows_1_export_to_finish( From 5334afcdb6d791b5de6b05a8e88c5a883e01157f Mon Sep 17 00:00:00 2001 From: pranaysb Date: Thu, 23 Jul 2026 07:56:19 +0530 Subject: [PATCH 06/11] test: disable pylint logging-too-few-args in test_export Assisted-by: Google Antigravity --- opentelemetry-sdk/tests/logs/test_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-sdk/tests/logs/test_export.py b/opentelemetry-sdk/tests/logs/test_export.py index 5d8b4328cea..b03e8068c40 100644 --- a/opentelemetry-sdk/tests/logs/test_export.py +++ b/opentelemetry-sdk/tests/logs/test_export.py @@ -328,7 +328,7 @@ def test_simple_log_record_processor_custom_single_obj(self): # formatting was applied # string msg with no args - getMessage bypasses formatting and sets the string directly - logger.warning("a string with a percent-s: %s") + logger.warning("a string with a percent-s: %s") # pylint: disable=logging-too-few-args # string msg with args - getMessage formats args into the msg logger.warning("a string with a percent-s: %s", "and arg") # non-string msg with args - getMessage stringifies msg and formats args into it From 7ab64fb5728a4388441907cf7fc14c6270f281df Mon Sep 17 00:00:00 2001 From: S B Pranay Date: Thu, 23 Jul 2026 13:12:12 +0530 Subject: [PATCH 07/11] Update opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py Co-authored-by: Lukas Hering <40302054+herin049@users.noreply.github.com> --- .../src/opentelemetry/sdk/_shared_internal/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py index fdfe373bea7..5a2a57f8fc0 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py @@ -118,8 +118,7 @@ def __init__( weak_reinit = weakref.WeakMethod(self._at_fork_reinit) def _after_in_child() -> None: - reinit = weak_reinit() - if reinit is not None: + if reinit := weak_reinit(): reinit() os.register_at_fork(after_in_child=_after_in_child) From 777f5567a662a1a340c95df5a6df1808045e6073 Mon Sep 17 00:00:00 2001 From: S B Pranay Date: Thu, 23 Jul 2026 13:13:53 +0530 Subject: [PATCH 08/11] Update opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py Co-authored-by: Lukas Hering <40302054+herin049@users.noreply.github.com> --- .../src/opentelemetry/sdk/metrics/_internal/export/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py index de73538aff1..8cce878cdba 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py @@ -532,8 +532,7 @@ def __init__( weak_at_fork = weakref.WeakMethod(self._at_fork_reinit) def _after_in_child() -> None: - at_fork = weak_at_fork() - if at_fork is not None: + if at_fork := weak_at_fork(): at_fork() os.register_at_fork(after_in_child=_after_in_child) From 92133f35a9337cd7e37286c13359200602df8af0 Mon Sep 17 00:00:00 2001 From: pranaysb Date: Thu, 23 Jul 2026 13:25:27 +0530 Subject: [PATCH 09/11] test: improve robust cleanup and skip logic in fork tests Assisted-by: Google Antigravity --- opentelemetry-sdk/tests/logs/test_export.py | 2 +- .../test_periodic_exporting_metric_reader.py | 41 +++++++++++-------- .../shared_internal/test_batch_processor.py | 38 +++++++++-------- 3 files changed, 45 insertions(+), 36 deletions(-) diff --git a/opentelemetry-sdk/tests/logs/test_export.py b/opentelemetry-sdk/tests/logs/test_export.py index b03e8068c40..5d8b4328cea 100644 --- a/opentelemetry-sdk/tests/logs/test_export.py +++ b/opentelemetry-sdk/tests/logs/test_export.py @@ -328,7 +328,7 @@ def test_simple_log_record_processor_custom_single_obj(self): # formatting was applied # string msg with no args - getMessage bypasses formatting and sets the string directly - logger.warning("a string with a percent-s: %s") # pylint: disable=logging-too-few-args + logger.warning("a string with a percent-s: %s") # string msg with args - getMessage formats args into the msg logger.warning("a string with a percent-s: %s", "and arg") # non-string msg with args - getMessage stringifies msg and formats args into it diff --git a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py index 7c13dad2593..105295e8a55 100644 --- a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py +++ b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py @@ -309,7 +309,7 @@ def test_metric_exporer_gc(self): "The PeriodicExportingMetricReader object created by this test wasn't garbage collected", ) - @pytest.mark.skipif(not hasattr(os, "fork"), reason="needs *nix") + @pytest.mark.skipif(not hasattr(os, "fork") or not hasattr(os, "register_at_fork"), reason="needs fork and register_at_fork") def test_garbage_collected_processor_does_not_crash_on_fork(self): exporter = FakeMetricsExporter( preferred_aggregation={ @@ -317,10 +317,13 @@ def test_garbage_collected_processor_does_not_crash_on_fork(self): }, ) processor = PeriodicExportingMetricReader(exporter) + w_ref = weakref.ref(processor) processor.shutdown() del processor gc.collect() + self.assertIsNone(w_ref()) + # The bug causes an unraisable exception to be printed to stderr. # We redirect stderr to a pipe before fork to capture it. r_fd, w_fd = os.pipe() @@ -329,24 +332,26 @@ def test_garbage_collected_processor_does_not_crash_on_fork(self): # We also temporarily restore the default sys.unraisablehook. Pytest overrides # it to capture exceptions in memory, which would be lost on os._exit(0). old_fd = os.dup(sys.stderr.fileno()) - os.dup2(w_fd, sys.stderr.fileno()) old_hook = sys.unraisablehook - sys.unraisablehook = sys.__unraisablehook__ - - pid = os.fork() - if pid == 0: - os.close(r_fd) - # os.fork() has already run the at_fork hooks. - os._exit(0) - - # Restore original stderr and hook in parent - sys.unraisablehook = old_hook - os.dup2(old_fd, sys.stderr.fileno()) - os.close(old_fd) - os.close(w_fd) - - _, status = os.waitpid(pid, 0) - self.assertEqual(status, 0) + + try: + os.dup2(w_fd, sys.stderr.fileno()) + sys.unraisablehook = sys.__unraisablehook__ + + pid = os.fork() + if pid == 0: + os.close(r_fd) + # os.fork() has already run the at_fork hooks. + os._exit(0) + + _, status = os.waitpid(pid, 0) + self.assertEqual(status, 0) + finally: + # Restore original stderr and hook in parent + sys.unraisablehook = old_hook + os.dup2(old_fd, sys.stderr.fileno()) + os.close(old_fd) + os.close(w_fd) os.set_blocking(r_fd, False) child_stderr = b"" diff --git a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py index 891643fc813..09e4d379a66 100644 --- a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py +++ b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py @@ -225,10 +225,13 @@ def test_garbage_collected_processor_does_not_crash_on_fork( ): exporter = Mock() processor = batch_processor_class(exporter) + w_ref = weakref.ref(processor) processor.shutdown() del processor gc.collect() + self.assertIsNone(w_ref()) + # The bug causes an unraisable exception to be printed to stderr. # We redirect stderr to a pipe before fork to capture it. r_fd, w_fd = os.pipe() @@ -237,24 +240,25 @@ def test_garbage_collected_processor_does_not_crash_on_fork( # We also temporarily restore the default sys.unraisablehook. Pytest overrides # it to capture exceptions in memory, which would be lost on os._exit(0). old_fd = os.dup(sys.stderr.fileno()) - os.dup2(w_fd, sys.stderr.fileno()) old_hook = sys.unraisablehook - sys.unraisablehook = sys.__unraisablehook__ - - pid = os.fork() - if pid == 0: - os.close(r_fd) - # os.fork() has already run the at_fork hooks. - os._exit(0) - - # Restore original stderr and hook in parent - sys.unraisablehook = old_hook - os.dup2(old_fd, sys.stderr.fileno()) - os.close(old_fd) - os.close(w_fd) - - _, status = os.waitpid(pid, 0) - assert status == 0 + + try: + os.dup2(w_fd, sys.stderr.fileno()) + sys.unraisablehook = sys.__unraisablehook__ + + pid = os.fork() + if pid == 0: + os.close(r_fd) + # os.fork() has already run the at_fork hooks. + os._exit(0) + + os.waitpid(pid, 0) + finally: + # Restore original stderr and hook in parent + sys.unraisablehook = old_hook + os.dup2(old_fd, sys.stderr.fileno()) + os.close(old_fd) + os.close(w_fd) os.set_blocking(r_fd, False) child_stderr = b"" From 6d7cab140e3cb0ed84a79c0f3f86faa69bfa5a42 Mon Sep 17 00:00:00 2001 From: pranaysb Date: Thu, 23 Jul 2026 13:25:55 +0530 Subject: [PATCH 10/11] test: fix assert usage in test_batch_processor Assisted-by: Google Antigravity --- opentelemetry-sdk/tests/shared_internal/test_batch_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py index 09e4d379a66..e03ae0f62b8 100644 --- a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py +++ b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py @@ -230,7 +230,7 @@ def test_garbage_collected_processor_does_not_crash_on_fork( del processor gc.collect() - self.assertIsNone(w_ref()) + assert w_ref() is None # The bug causes an unraisable exception to be printed to stderr. # We redirect stderr to a pipe before fork to capture it. From 9791fbcb46cd3dd14b9c27ad0667f96d8eab20a3 Mon Sep 17 00:00:00 2001 From: pranaysb Date: Thu, 23 Jul 2026 13:44:44 +0530 Subject: [PATCH 11/11] style: fix trailing whitespace in tests again Assisted-by: Google Antigravity --- .../metrics/test_periodic_exporting_metric_reader.py | 9 ++++++--- .../tests/shared_internal/test_batch_processor.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py index 105295e8a55..1a2a1b319f3 100644 --- a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py +++ b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py @@ -309,7 +309,10 @@ def test_metric_exporer_gc(self): "The PeriodicExportingMetricReader object created by this test wasn't garbage collected", ) - @pytest.mark.skipif(not hasattr(os, "fork") or not hasattr(os, "register_at_fork"), reason="needs fork and register_at_fork") + @pytest.mark.skipif( + not hasattr(os, "fork") or not hasattr(os, "register_at_fork"), + reason="needs fork and register_at_fork", + ) def test_garbage_collected_processor_does_not_crash_on_fork(self): exporter = FakeMetricsExporter( preferred_aggregation={ @@ -333,7 +336,7 @@ def test_garbage_collected_processor_does_not_crash_on_fork(self): # it to capture exceptions in memory, which would be lost on os._exit(0). old_fd = os.dup(sys.stderr.fileno()) old_hook = sys.unraisablehook - + try: os.dup2(w_fd, sys.stderr.fileno()) sys.unraisablehook = sys.__unraisablehook__ @@ -343,7 +346,7 @@ def test_garbage_collected_processor_does_not_crash_on_fork(self): os.close(r_fd) # os.fork() has already run the at_fork hooks. os._exit(0) - + _, status = os.waitpid(pid, 0) self.assertEqual(status, 0) finally: diff --git a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py index e03ae0f62b8..91500155cdb 100644 --- a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py +++ b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py @@ -241,7 +241,7 @@ def test_garbage_collected_processor_does_not_crash_on_fork( # it to capture exceptions in memory, which would be lost on os._exit(0). old_fd = os.dup(sys.stderr.fileno()) old_hook = sys.unraisablehook - + try: os.dup2(w_fd, sys.stderr.fileno()) sys.unraisablehook = sys.__unraisablehook__ @@ -251,7 +251,7 @@ def test_garbage_collected_processor_does_not_crash_on_fork( os.close(r_fd) # os.fork() has already run the at_fork hooks. os._exit(0) - + os.waitpid(pid, 0) finally: # Restore original stderr and hook in parent