From 1111594513cfb0a4709153cc1c398acc990b50a9 Mon Sep 17 00:00:00 2001 From: stlc-bot Date: Fri, 25 Sep 2026 15:25:05 +0000 Subject: [PATCH 1/2] test(openapi): fix two spec mismatches that fail the mock-server tests in every SDK Stainless-Generated-From: 18ae3d43611f80dc1b00b1a73234819983af85c1 --- scripts/mock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mock b/scripts/mock index 45416b90..86001174 100755 --- a/scripts/mock +++ b/scripts/mock @@ -16,7 +16,7 @@ elif [ -n "$STAINLESS_OPENAPI_SPEC_URL" ]; then URL="$STAINLESS_OPENAPI_SPEC_URL" else # Embedded OpenAPI spec (base64-encoded, gzipped) - EMBEDDED_SPEC="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" + EMBEDDED_SPEC="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" SPEC_TMPFILE="$(mktemp)" trap 'rm -f "$SPEC_TMPFILE"' EXIT echo "$EMBEDDED_SPEC" | base64 --decode | gunzip > "$SPEC_TMPFILE" From 7d613f9d5e40d23b6d08a2d053dcca210cee4392 Mon Sep 17 00:00:00 2001 From: Vinicius Mello Date: Fri, 25 Sep 2026 12:47:16 -0300 Subject: [PATCH 2/2] fix(tracing): finalize traced generators at most once, and run the test suite in CI Traced sync and async generators no longer leak their trace into later calls, publish twice, or publish from garbage collection. Abandoned streams end without publishing; an async stream that is aclose()d or cancelled publishes a partial trace. Integration tests skip when their optional SDK isn't installed and no longer leak global patches or background uploads across tests. The new .github/workflows/test.yml runs the full suite (py3.9) and the integrations with optional SDKs (py3.12), gated by one `required` check. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01U8uKEMAsum6Dza2gui4Tis --- .github/workflows/test.yml | 83 ++ src/openlayer/lib/tracing/tracer.py | 443 +++++++---- tests/integrations/test_claude_agent_sdk.py | 38 +- .../integrations/test_langchain_callback.py | 3 + ...test_async_openai_embedding_integration.py | 4 +- tests/test_bedrock_integration.py | 4 + tests/test_litellm_integration.py | 8 +- tests/test_openai_embedding_integration.py | 4 +- tests/test_tracing_core.py | 730 +++++++++++++++++- 9 files changed, 1180 insertions(+), 137 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..6f8dc192 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,83 @@ +# Separate from the generated ci.yml. stlc keeps workflow files across builds, but +# `stlc build --rewrite-scaffold` would drop this one; re-add it after such a run. +name: Tests +on: + # No job-level `if:` (unlike ci.yml): a job skipped on pull_request counts as passing + # for required checks, and the PRs to gate (promote, release-please) are same-repo. + pull_request: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: test-${{ github.ref }} + # Only PR runs: a cancelled run on main would fail the required check for a good commit. + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + test: + timeout-minutes: 10 + name: full suite (py3.9) + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Run tests + run: ./scripts/test + + test-integrations: + timeout-minutes: 10 + name: integrations with optional SDKs (py3.12) + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 + + # The `test` job skips integration tests whose optional package isn't + # installed; this one installs them. Python 3.12 because claude-agent-sdk + # needs 3.10+, and requirements-dev.lock only resolves on 3.9, so only the + # test tools are pinned from it. + - name: Install dependencies + run: | + uv venv --python 3.12 + grep -E '^(pytest|pytest-asyncio|pytest-xdist|respx|dirty-equals|time-machine)==' requirements-dev.lock > test-tools.txt + # Resolve as of a day ago: a release still uploading its wheels forces a + # source build that can fail (SQLAlchemy 2.1.0 did). + # google-adk<2: 2.x wraps callback exceptions and fails test_user_exception_propagates_without_chaining. + uv pip install --exclude-newer "$(date -u -d '1 day ago' +%Y-%m-%dT%H:%M:%SZ)" \ + -e '.[aiohttp]' -r test-tools.txt \ + openai boto3 claude-agent-sdk 'google-adk<2' google-genai langchain-core wrapt \ + azure-cognitiveservices-speech + + # Generated API tests need the mock server and already run in `test`. + - name: Run integration tests + run: .venv/bin/pytest --ignore=tests/api_resources + + # The one check to require in rulesets, so jobs can be added or renamed freely. + # The integrations job stays advisory until its optional dependencies are pinned. + # always(), not !cancelled(): a skipped required check would count as passing. + required: + if: always() + needs: [test] + runs-on: ubuntu-latest + steps: + - run: test "${{ needs.test.result }}" = success diff --git a/src/openlayer/lib/tracing/tracer.py b/src/openlayer/lib/tracing/tracer.py index 171ccbd4..f93f4e66 100644 --- a/src/openlayer/lib/tracing/tracer.py +++ b/src/openlayer/lib/tracing/tracer.py @@ -8,6 +8,7 @@ import json import logging import os +import sys import threading import time import traceback @@ -834,9 +835,11 @@ def __init__(self): self._step = None self._is_root_step = False self._token = None + self._trace = None self._trace_token = None self._output_chunks = [] self._trace_initialized = False + self._finalized = False self._unresolved_promote = {} self._captured_context = ( None # Capture context for ASGI compatibility @@ -845,7 +848,92 @@ def __init__(self): def __iter__(self): return self + def __del__(self): + # Drop the wrapped generator first, so CPython finalizes it exactly + # once (running its finally, reporting what it raises) while the + # stream's step is still current; then unwind. + try: + if self._trace_initialized and not self._finalized: + gen, self._original_gen = self._original_gen, None + del gen + _release_abandoned_generator(self) + except Exception: + pass + + def close(self): + # Closing abandons the stream: unwind, don't publish. CPython also + # calls close() on a `yield from` delegate, including from GC, + # where publishing can deadlock on a lock the thread holds. + if not self._trace_initialized: + self._finalized = True # like a generator closed before it starts + return + if self._finalized: + return + if self._original_gen.gi_running: + raise ValueError("generator already executing") + try: + self._original_gen.close() + finally: + # Another thread may have entered the body since the check. + if self._original_gen.gi_frame is None: + _release_abandoned_generator(self) + + def _finalize(self, output, promote_output=False): + if self._finalized: + return + self._finalized = True + finalize_kwargs = dict( + step=self._step, + token=self._token, + trace=self._trace, + trace_token=self._trace_token, + is_root_step=self._is_root_step, + step_name=step_name, + inputs=self._inputs, + output=output, + inference_pipeline_id=inference_pipeline_id, + on_flush_failure=on_flush_failure, + ) + if self._captured_context is not None: + # Run promote inside captured context so + # update_current_trace can find the trace ContextVar + if promote_output: + self._captured_context.run( + _apply_promote_output, + output, + self._unresolved_promote, + ) + self._captured_context.run( + _finalize_generator_step, **finalize_kwargs + ) + # Finalizing inside the captured copy can't reset the + # caller's ContextVars; left set, the next @trace call + # on this thread nests into this finished trace and is + # never published. + _release_generator_context( + self._step, + self._token, + self._trace, + self._trace_token, + self._is_root_step, + ) + else: + if promote_output: + _apply_promote_output(output, self._unresolved_promote) + _finalize_generator_step(**finalize_kwargs) + def __next__(self): + if self._finalized: # finished, or closed (even before it started) + if self._trace_initialized: + # It may have ended in another context; unwind this one. + _release_generator_context( + self._step, + self._token, + self._trace, + self._trace_token, + self._is_root_step, + ) + raise StopIteration # Initialize tracing on first iteration only if not self._trace_initialized: self._original_gen = func(*func_args, **func_kwargs) @@ -873,6 +961,8 @@ def __next__(self): self._unresolved_promote = _apply_promote_kwargs( self._inputs, promote ) + self._trace = get_current_trace() + self._step._is_stream = True self._trace_initialized = True try: @@ -883,74 +973,28 @@ def __next__(self): return chunk except StopIteration: # Finalize trace when generator is exhausted - # Use captured context to ensure we have access to the trace - output = _join_output_chunks(self._output_chunks) - if self._captured_context: - # Run promote inside captured context so - # update_current_trace can find the trace ContextVar - self._captured_context.run( - _apply_promote_output, - output, - self._unresolved_promote, - ) - self._captured_context.run( - _finalize_sync_generator_step, - step=self._step, - token=self._token, - trace_token=self._trace_token, - is_root_step=self._is_root_step, - step_name=step_name, - inputs=self._inputs, - output=output, - inference_pipeline_id=inference_pipeline_id, - on_flush_failure=on_flush_failure, - ) - else: - _apply_promote_output( - output, self._unresolved_promote - ) - _finalize_sync_generator_step( - step=self._step, - token=self._token, - trace_token=self._trace_token, - is_root_step=self._is_root_step, - step_name=step_name, - inputs=self._inputs, - output=output, - inference_pipeline_id=inference_pipeline_id, - on_flush_failure=on_flush_failure, - ) + self._finalize( + _join_output_chunks(self._output_chunks), + promote_output=True, + ) raise - except Exception as exc: - # Handle exceptions - if self._step: + except BaseException as exc: + if self._original_gen.gi_frame is not None: + # The body is still live, so this isn't its exception (e.g. + # another consumer's concurrent next()): don't end the stream, + # but unwind this context if it's the one that started it. + _release_generator_context( + self._step, + self._token, + self._trace, + self._trace_token, + self._is_root_step, + ) + raise + # Handle exceptions, including KeyboardInterrupt + if self._step and not self._finalized: _log_step_exception(self._step, exc) - output = _join_output_chunks(self._output_chunks) - if self._captured_context: - self._captured_context.run( - _finalize_sync_generator_step, - step=self._step, - token=self._token, - trace_token=self._trace_token, - is_root_step=self._is_root_step, - step_name=step_name, - inputs=self._inputs, - output=output, - inference_pipeline_id=inference_pipeline_id, - on_flush_failure=on_flush_failure, - ) - else: - _finalize_sync_generator_step( - step=self._step, - token=self._token, - trace_token=self._trace_token, - is_root_step=self._is_root_step, - step_name=step_name, - inputs=self._inputs, - output=output, - inference_pipeline_id=inference_pipeline_id, - on_flush_failure=on_flush_failure, - ) + self._finalize(_join_output_chunks(self._output_chunks)) raise return TracedSyncGenerator() @@ -1164,15 +1208,97 @@ def __init__(self): self._step = None self._is_root_step = False self._token = None + self._trace = None self._trace_token = None self._output_chunks = [] self._trace_initialized = False + self._finalized = False self._unresolved_promote = {} + self._captured_context = None def __aiter__(self): return self + def __del__(self): + _release_abandoned_generator(self) + + async def aclose(self): + # Unlike sync close(), aclose() publishes: nothing calls it on + # this wrapper implicitly. But it can be awaited while a coroutine + # is torn down (an aclosing() exit in a task being garbage- + # collected); treat that as abandonment and don't publish. + # This also covers explicitly closing an enclosing generator whose + # finally awaits this aclose(), like sync close() of a delegate. + if _handling_generator_exit(): + _release_abandoned_generator(self) + return + if not self._trace_initialized: + self._finalized = True # like a generator closed before it starts + return + if self._finalized: + return + if self._original_gen.ag_running: + raise RuntimeError( + "aclose(): asynchronous generator is already running" + ) + try: + await self._original_gen.aclose() + except BaseException as exc: + _log_step_exception(self._step, exc) + raise + finally: + self._finalize(_join_output_chunks(self._output_chunks)) + + def _finalize(self, output, promote_output=False): + if self._finalized: + return + self._finalized = True + # Trace completion publishes whatever trace is current, so + # when this task isn't running the stream's trace (it was + # started in another task, or its parent already finished), + # finalize in the context the stream was traced in. + if get_current_trace() is self._trace: + self._complete(output, promote_output) + else: + self._captured_context.run( + self._complete, output, promote_output + ) + _release_generator_context( + self._step, + self._token, + self._trace, + self._trace_token, + self._is_root_step, + ) + + def _complete(self, output, promote_output): + if promote_output: + _apply_promote_output(output, self._unresolved_promote) + _finalize_generator_step( + step=self._step, + token=self._token, + trace=self._trace, + trace_token=self._trace_token, + is_root_step=self._is_root_step, + step_name=step_name, + inputs=self._inputs, + output=output, + inference_pipeline_id=inference_pipeline_id, + on_flush_failure=on_flush_failure, + ) + async def __anext__(self): + if self._finalized: # finished, abandoned, or closed early + if self._trace_initialized: + # It may have ended in another context; unwind this one. + _release_generator_context( + self._step, + self._token, + self._trace, + self._trace_token, + self._is_root_step, + ) + raise StopAsyncIteration # Initialize tracing on first iteration only if not self._trace_initialized: self._original_gen = func(*func_args, **func_kwargs) @@ -1200,6 +1326,9 @@ async def __anext__(self): self._unresolved_promote = _apply_promote_kwargs( self._inputs, promote ) + self._trace = get_current_trace() + self._step._is_stream = True + self._captured_context = contextvars.copy_context() self._trace_initialized = True try: @@ -1208,38 +1337,34 @@ async def __anext__(self): return chunk except StopAsyncIteration: # Finalize trace when generator is exhausted - output = _join_output_chunks(self._output_chunks) - _apply_promote_output( - output, self._unresolved_promote - ) - _finalize_async_generator_step( - step=self._step, - token=self._token, - trace_token=self._trace_token, - is_root_step=self._is_root_step, - step_name=step_name, - inputs=self._inputs, - output=output, - inference_pipeline_id=inference_pipeline_id, - on_flush_failure=on_flush_failure, + self._finalize( + _join_output_chunks(self._output_chunks), + promote_output=True, ) raise - except Exception as exc: - # Handle exceptions - if self._step: - _log_step_exception(self._step, exc) - output = _join_output_chunks(self._output_chunks) - _finalize_async_generator_step( - step=self._step, - token=self._token, - trace_token=self._trace_token, - is_root_step=self._is_root_step, - step_name=step_name, - inputs=self._inputs, - output=output, - inference_pipeline_id=inference_pipeline_id, - on_flush_failure=on_flush_failure, + except GeneratorExit: + # The awaiting coroutine is being destroyed (e.g. its task + # was garbage-collected): abandoned, so release only. + _release_abandoned_generator(self) + raise + except BaseException as exc: + if self._original_gen.ag_frame is not None: + # The body is still live, so this isn't its exception (e.g. + # another task's concurrent __anext__): don't end the stream, + # but unwind this context if it's the one that started it. + _release_generator_context( + self._step, + self._token, + self._trace, + self._trace_token, + self._is_root_step, ) + raise + # Handle exceptions, including CancelledError from a + # timeout or client disconnect in this task + if self._step and not self._finalized: + _log_step_exception(self._step, exc) + self._finalize(_join_output_chunks(self._output_chunks)) raise return TracedAsyncGenerator() @@ -2119,9 +2244,10 @@ def _handle_streaming_failure( # ----------------------------- Helper functions for trace decorators ----------------------------- # -def _log_step_exception(step: steps.Step, exception: Exception) -> None: +def _log_step_exception(step: steps.Step, exception: BaseException) -> None: """Log exception metadata to a step.""" - step.log(metadata={"Exceptions": str(exception)}) + # str() is empty for CancelledError, KeyboardInterrupt and message-less raises. + step.log(metadata={"Exceptions": str(exception) or type(exception).__name__}) def _process_wrapper_inputs_and_outputs( @@ -2337,46 +2463,102 @@ def _finalize_step_logging( # ----------------------------- Generator specific functions ----------------------------- # -def _finalize_sync_generator_step( +def _owns_current_step( + step: steps.Step, trace: Optional["traces.Trace"], is_root_step: bool +) -> bool: + """Whether a finishing generator should unwind the current step. + + Generators can finish out of stack order (after their parent returned, or from + another task), so only unwind a step that is still ours; unconditionally resetting + would restore a parent that already finished. A finishing root also takes whatever + is still current under its finished trace (a stream started after it, a consumer). + """ + current = get_current_step() + if current is step or (is_root_step and get_current_trace() is trace): + return True + # A stream started under this one and still current (e.g. zip of two streams) + # would otherwise stay current, or later bring this finished step back. For a + # non-root stream, open function steps are left alone: their create_step exit + # unwinds them. + return ( + current is not None + and getattr(current, "_is_stream", False) + and _is_descendant(current, of=step) + ) + + +def _is_descendant(candidate: steps.Step, of: steps.Step) -> bool: + pending = list(of.steps) + while pending: + nested = pending.pop() + if nested is candidate: + return True + pending.extend(nested.steps) + return False + + +def _reset_contextvar_quietly(var: "contextvars.ContextVar[Any]", token: Any) -> None: + # Generator tokens may belong to another context (finished in another task or + # thread) or be spent already (unwound by an earlier release); neither is an error. + if token is None: + return + try: + var.reset(token) + except (ValueError, RuntimeError): + pass + + +def _release_generator_context( step: steps.Step, token: Any, + trace: Optional["traces.Trace"], trace_token: Any, is_root_step: bool, - step_name: str, - inputs: dict, - output: Any, - inference_pipeline_id: Optional[str] = None, - on_flush_failure: Optional[OnFlushFailureCallback] = None, ) -> None: - """Finalize sync generator step - called when generator is consumed.""" - try: - _current_step.reset(token) - except ValueError: - # Context variable was created in a different context (e.g., different thread) - # This can happen in async/multi-threaded environments like FastAPI/OpenWebUI - # We can safely ignore this as the step finalization will still complete - logger.debug( - "Context variable reset failed - generator consumed in different context" - ) + """Unwind a traced generator's ContextVars in the current context, if still ours.""" + if _owns_current_step(step, trace, is_root_step): + _reset_contextvar_quietly(_current_step, token) + if is_root_step and get_current_trace() is trace: + _reset_contextvar_quietly(_current_trace, trace_token) + + +def _handling_generator_exit() -> bool: + """Whether a GeneratorExit is being handled, i.e. a coroutine or generator is closing.""" + exc = sys.exc_info()[1] + for _ in range(32): # __context__ chains are short; bound the walk anyway + if exc is None: + return False + if isinstance(exc, GeneratorExit): + return True + exc = exc.__context__ + return False - _finalize_step_logging( - step=step, inputs=inputs, output=output, start_time=step.start_time - ) - _handle_trace_completion( - is_root_step=is_root_step, - step_name=step_name, - inference_pipeline_id=inference_pipeline_id, - on_flush_failure=on_flush_failure, - ) +def _release_abandoned_generator(gen: Any) -> None: + """End an abandoned stream: unwind its ContextVars without publishing it. - if is_root_step: - _safe_reset_contextvar(_current_trace, trace_token) + Called from __del__, close(), aclose() during coroutine teardown, and when the + coroutine awaiting __anext__ is destroyed. These can run inside garbage collection, on + a thread that may already hold a non-reentrant lock (ThreadPoolExecutor.submit, an + HTTP pool), so this must not publish, log or take locks. + """ + try: + if not gen._trace_initialized or gen._finalized: + return + gen._finalized = True + if sys.is_finalizing(): + return + _release_generator_context( + gen._step, gen._token, gen._trace, gen._trace_token, gen._is_root_step + ) + except Exception: + pass -def _finalize_async_generator_step( +def _finalize_generator_step( step: steps.Step, token: Any, + trace: Optional["traces.Trace"], trace_token: Any, is_root_step: bool, step_name: str, @@ -2385,11 +2567,14 @@ def _finalize_async_generator_step( inference_pipeline_id: Optional[str] = None, on_flush_failure: Optional[OnFlushFailureCallback] = None, ) -> None: - """Finalize async generator step - called when generator is consumed.""" - _current_step.reset(token) + """Finish a traced generator's step and publish its trace if it is a root.""" + if _owns_current_step(step, trace, is_root_step): + _reset_contextvar_quietly(_current_step, token) + _finalize_step_logging( step=step, inputs=inputs, output=output, start_time=step.start_time ) + _handle_trace_completion( is_root_step=is_root_step, step_name=step_name, @@ -2397,8 +2582,8 @@ def _finalize_async_generator_step( on_flush_failure=on_flush_failure, ) - if is_root_step: - _safe_reset_contextvar(_current_trace, trace_token) + if is_root_step and get_current_trace() is trace: + _reset_contextvar_quietly(_current_trace, trace_token) def _join_output_chunks(output_chunks: List[Any]) -> str: diff --git a/tests/integrations/test_claude_agent_sdk.py b/tests/integrations/test_claude_agent_sdk.py index 78e7df71..8b66a5ea 100644 --- a/tests/integrations/test_claude_agent_sdk.py +++ b/tests/integrations/test_claude_agent_sdk.py @@ -6,7 +6,8 @@ # ruff: noqa: ARG001 import asyncio -from typing import Any, List +import inspect +from typing import Any, List, Generator from unittest.mock import patch import pytest @@ -33,6 +34,27 @@ def _disable_publish(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("OPENLAYER_DISABLE_PUBLISH", "true") monkeypatch.setenv("OPENLAYER_API_KEY", "fake") monkeypatch.setattr(ol_tracer, "_publish", False, raising=False) + # Publish inline so tests that capture the upload don't race the executor. + monkeypatch.setitem(ol_tracer._tracer_config, "background_publish_enabled", False) + + +@pytest.fixture(autouse=True) +def _undo_sdk_patches(monkeypatch: pytest.MonkeyPatch) -> Generator[None, None, None]: + """trace_claude_agent_sdk() patches the SDK globally; undo it after each test.""" + try: + import claude_agent_sdk as cas + except ImportError: + yield + return + monkeypatch.setattr(cas, "query", cas.query) + for name in ("__init__", "query", "receive_response"): + monkeypatch.setattr( + cas.ClaudeSDKClient, name, inspect.getattr_static(cas.ClaudeSDKClient, name) + ) + yield + # Not via monkeypatch: some tests delete the flag themselves. + if "_openlayer_patched" in vars(cas.ClaudeSDKClient): + del cas.ClaudeSDKClient._openlayer_patched def _capture_trace_publish(): @@ -65,6 +87,7 @@ def test_traced_query_emits_root_agent_step_with_cost_and_tokens(): The root AGENT step gets cost/tokens/session_id populated from the final ``ResultMessage``. """ + pytest.importorskip("claude_agent_sdk") from openlayer.lib.integrations.claude_agent_sdk import traced_query messages = [ @@ -178,6 +201,7 @@ async def run(): def test_assistant_message_emits_chat_completion_step(): """Each AssistantMessage becomes a CHAT_COMPLETION child of the root step.""" + pytest.importorskip("claude_agent_sdk") from openlayer.lib.integrations.claude_agent_sdk import traced_query messages = [ @@ -242,6 +266,7 @@ def _extract_hook_callbacks(options, event: str): def test_tool_call_creates_tool_step_with_input_and_output(): """A tool call yields a TOOL step with input/output/latency/tool_use_id.""" + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk as cas from openlayer.lib.integrations.claude_agent_sdk import traced_query @@ -311,6 +336,7 @@ async def run(): def test_mcp_tool_name_is_parsed_into_metadata(): """A tool named ``mcp__playwright__browser_click`` records the parsed metadata.""" + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk as cas from openlayer.lib.integrations.claude_agent_sdk import traced_query @@ -375,6 +401,7 @@ async def run(): def test_subagent_messages_nest_under_agent_tool_step(): """A message with ``parent_tool_use_id`` nests under the spawning Agent ToolStep.""" + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk as cas from openlayer.lib.integrations.claude_agent_sdk import traced_query @@ -481,6 +508,7 @@ def test_subagent_internal_tool_calls_nest_under_agent_step(): message says ``[tool call: Grep]`` but the actual Grep TOOL step was being created as a child of root instead of the ``Agent: ...`` AGENT step. """ + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk as cas from openlayer.lib.integrations.claude_agent_sdk import traced_query @@ -612,6 +640,7 @@ async def run(): def test_result_message_error_subtype_marks_root_step(): """An error ResultMessage subtype is reflected on the root step's metadata.""" + pytest.importorskip("claude_agent_sdk") from openlayer.lib.integrations.claude_agent_sdk import traced_query messages = [ @@ -649,6 +678,7 @@ async def run(): def test_post_tool_use_failure_marks_tool_step_as_error(): """PostToolUseFailure fires instead of PostToolUse — the tool step is marked errored.""" + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk as cas from openlayer.lib.integrations.claude_agent_sdk import traced_query @@ -708,6 +738,7 @@ async def run(): def test_user_hooks_compose_with_openlayer_hooks(): """User-provided hooks run alongside ours; neither replaces the other.""" + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk as cas from openlayer.lib.integrations.claude_agent_sdk import traced_query @@ -785,6 +816,7 @@ async def run(): def test_mcp_env_is_stripped_from_agent_config_metadata(): """``env`` and ``headers`` of MCP server configs must be redacted.""" + pytest.importorskip("claude_agent_sdk") from openlayer.lib.integrations.claude_agent_sdk import traced_query messages = [ @@ -833,6 +865,7 @@ async def run(): def test_trace_claude_agent_sdk_patches_module_query(): """``trace_claude_agent_sdk()`` monkey-patches ``claude_agent_sdk.query``.""" + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk from openlayer.lib.integrations.claude_agent_sdk import trace_claude_agent_sdk @@ -854,6 +887,7 @@ def test_trace_claude_agent_sdk_patches_module_query(): def test_trace_claude_agent_sdk_config_persists(): """Init kwargs are persisted into the module-level config.""" + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk from openlayer.lib.integrations import claude_agent_sdk as integration @@ -882,6 +916,7 @@ def test_trace_claude_agent_sdk_config_persists(): def test_trace_claude_agent_sdk_patches_claude_sdk_client(): """``trace_claude_agent_sdk()`` also patches ``ClaudeSDKClient.query`` / ``.receive_response``.""" + pytest.importorskip("claude_agent_sdk") import claude_agent_sdk from openlayer.lib.integrations.claude_agent_sdk import trace_claude_agent_sdk @@ -912,6 +947,7 @@ def test_trace_claude_agent_sdk_patches_claude_sdk_client(): def test_wrapped_stream_yields_identical_messages_in_identical_order(): """The wrapper is a pure observer — output must equal the underlying stream.""" + pytest.importorskip("claude_agent_sdk") from openlayer.lib.integrations.claude_agent_sdk import traced_query original_messages = [ diff --git a/tests/lib/integrations/test_langchain_callback.py b/tests/lib/integrations/test_langchain_callback.py index 59226c44..3b4d4ee1 100644 --- a/tests/lib/integrations/test_langchain_callback.py +++ b/tests/lib/integrations/test_langchain_callback.py @@ -52,6 +52,9 @@ def _disable_publish(monkeypatch: pytest.MonkeyPatch) -> None: from openlayer.lib.tracing import tracer as _tracer monkeypatch.setattr(_tracer, "_publish", False, raising=False) + # Process traces inline: a background upload outlives this fixture, runs + # after _publish is restored, and builds a real client for later tests. + monkeypatch.setitem(_tracer._tracer_config, "background_publish_enabled", False) def _tool_call(name: str, args: dict, call_id: str) -> dict: diff --git a/tests/test_async_openai_embedding_integration.py b/tests/test_async_openai_embedding_integration.py index 1eaa9fda..807b6926 100644 --- a/tests/test_async_openai_embedding_integration.py +++ b/tests/test_async_openai_embedding_integration.py @@ -7,6 +7,8 @@ import asyncio from unittest.mock import Mock, AsyncMock, MagicMock, patch +import pytest + class TestOpenAIAsyncEmbedding: def _fake_response(self, embeddings, prompt_tokens=4, model="text-embedding-3-small"): @@ -117,7 +119,7 @@ def test_handle_embedding_async_labels_azure_clients_correctly(self) -> None: assert kwargs["provider"] == "Azure" def test_trace_async_openai_patches_embeddings_create(self) -> None: - import openai # pyright: ignore[reportMissingImports] + openai = pytest.importorskip("openai") from openlayer.lib.integrations.async_openai_tracer import trace_async_openai diff --git a/tests/test_bedrock_integration.py b/tests/test_bedrock_integration.py index 1b53bef5..7653065f 100644 --- a/tests/test_bedrock_integration.py +++ b/tests/test_bedrock_integration.py @@ -8,6 +8,10 @@ import json from unittest.mock import MagicMock, patch +import pytest + +pytest.importorskip("boto3") # bedrock_tracer needs boto3 (and botocore, its dependency) + class TestBedrockChatRegression: """Lock in existing chat-completion behaviour before refactoring.""" diff --git a/tests/test_litellm_integration.py b/tests/test_litellm_integration.py index c01e2372..c6feed44 100644 --- a/tests/test_litellm_integration.py +++ b/tests/test_litellm_integration.py @@ -30,7 +30,7 @@ def test_trace_litellm_raises_import_error_without_dependency(self): assert "pip install litellm" in str(exc_info.value) # type: ignore @patch('openlayer.lib.integrations.litellm_tracer.HAVE_LITELLM', True) - @patch('openlayer.lib.integrations.litellm_tracer.litellm') + @patch('openlayer.lib.integrations.litellm_tracer.litellm', create=True) def test_trace_litellm_patches_completion(self, mock_litellm: Mock) -> None: """Test that trace_litellm successfully patches litellm.completion.""" from openlayer.lib.integrations.litellm_tracer import trace_litellm @@ -261,7 +261,7 @@ def test_extract_litellm_metadata(self): assert metadata == expected_metadata @patch('openlayer.lib.integrations.litellm_tracer.HAVE_LITELLM', True) - @patch('openlayer.lib.integrations.litellm_tracer.litellm') + @patch('openlayer.lib.integrations.litellm_tracer.litellm', create=True) def test_detect_provider_with_litellm_method(self, mock_litellm: Mock) -> None: """Test provider detection using LiteLLM's get_llm_provider method.""" from openlayer.lib.integrations.litellm_tracer import detect_provider_from_response @@ -281,7 +281,7 @@ class TestLiteLLMEmbedding: """Embedding calls must be traced via add_embedding_step_to_trace.""" @patch("openlayer.lib.integrations.litellm_tracer.HAVE_LITELLM", True) - @patch("openlayer.lib.integrations.litellm_tracer.litellm") + @patch("openlayer.lib.integrations.litellm_tracer.litellm", create=True) def test_trace_litellm_patches_embedding(self, mock_litellm: Mock) -> None: from openlayer.lib.integrations import litellm_tracer @@ -428,7 +428,7 @@ def test_handle_embedding_failure_does_not_break_client(self) -> None: assert result is fake_response @patch("openlayer.lib.integrations.litellm_tracer.HAVE_LITELLM", True) - @patch("openlayer.lib.integrations.litellm_tracer.litellm") + @patch("openlayer.lib.integrations.litellm_tracer.litellm", create=True) def test_completion_path_unchanged_after_embedding_patch( self, mock_litellm: Mock ) -> None: diff --git a/tests/test_openai_embedding_integration.py b/tests/test_openai_embedding_integration.py index 0d896df5..06a0f2f1 100644 --- a/tests/test_openai_embedding_integration.py +++ b/tests/test_openai_embedding_integration.py @@ -6,6 +6,8 @@ from unittest.mock import Mock, MagicMock, patch +import pytest + class TestOpenAISyncEmbedding: """Sync OpenAI client.embeddings.create must be traced.""" @@ -112,7 +114,7 @@ def test_handle_embedding_labels_azure_clients_correctly(self) -> None: def test_trace_openai_patches_embeddings_create(self) -> None: """After trace_openai, client.embeddings.create is replaced.""" - import openai # pyright: ignore[reportMissingImports] + openai = pytest.importorskip("openai") from openlayer.lib.integrations.openai_tracer import trace_openai diff --git a/tests/test_tracing_core.py b/tests/test_tracing_core.py index 628d1f5b..3ad1cab8 100644 --- a/tests/test_tracing_core.py +++ b/tests/test_tracing_core.py @@ -8,8 +8,13 @@ # pyright: reportUnknownMemberType=false # pyright: reportUnknownArgumentType=false +import gc +import sys import asyncio -from typing import Any, Set, Dict, List, Generator +import threading +import contextlib +import contextvars +from typing import Any, Set, Dict, List, Generator, AsyncGenerator from unittest.mock import patch import pytest @@ -65,6 +70,7 @@ def generator_function(n: int) -> Generator[int, None, None]: gen = generator_function(3) results = list(gen) assert results == [0, 1, 2] + assert tracer.get_current_trace() is None @patch.object(tracer, "_publish", False) def test_nested_tracing(self) -> None: @@ -914,3 +920,725 @@ def test_add_embedding_step_to_trace_uses_default_name(self) -> None: step_type=enums.StepType.EMBEDDING, name="Embedding", ) + + +@pytest.fixture +def published() -> Generator[List[Any], None, None]: + """Publish synchronously and capture (root step name, pipeline id) per trace.""" + captured: List[Any] = [] + + def capture(trace: traces.Trace, pipeline_id: Any, *args: Any) -> None: + captured.append((trace.steps[0].name, pipeline_id)) + + with patch.object(tracer, "_publish", True), patch.object( + tracer, "_upload_and_publish_trace", side_effect=capture + ), patch.dict(tracer._tracer_config, {"background_publish_enabled": False}): + yield captured + + +class TestGeneratorLifecycle: + """Traced generators publish at most once (abandoned streams never) and never leak context.""" + + def setup_method(self) -> None: + tracer._tracer_config.clear() + tracer._client = None + # A lifecycle regression leaks these; don't let it cascade into other tests. + tracer._current_step.set(None) + tracer._current_trace.set(None) + + def teardown_method(self) -> None: + tracer._tracer_config.clear() + tracer._client = None + tracer._current_step.set(None) + tracer._current_trace.set(None) + + def test_sync_early_break_releases_context(self, published: List[Any]) -> None: + """An abandoned stream's partial trace is dropped, but later calls publish.""" + @tracer.trace() + def stream() -> Generator[int, None, None]: + yield from range(3) + + @tracer.trace() + def after() -> int: + return 1 + + for _ in stream(): + break + after() + + assert published == [("after", None)] + assert tracer.get_current_trace() is None + assert tracer.get_current_step() is None + + def test_sync_next_after_exhaustion_publishes_once(self, published: List[Any]) -> None: + @tracer.trace() + def stream() -> Generator[int, None, None]: + yield from range(2) + + gen = stream() + assert list(gen) == [0, 1] + with pytest.raises(StopIteration): + next(gen) + + assert published == [("stream", None)] + + @pytest.mark.parametrize("finish", ["close", "exhaust"]) + def test_sync_child_finished_after_parent_returns(self, published: List[Any], finish: str) -> None: + holder: Dict[str, Any] = {} + + @tracer.trace() + def child() -> Generator[int, None, None]: + yield from range(3) + + @tracer.trace() + def parent() -> None: + gen = child() + next(gen) + holder["gen"] = gen + + @tracer.trace() + def after() -> int: + return 1 + + parent() + if finish == "close": + holder["gen"].close() + else: + list(holder["gen"]) + after() + + assert published == [("parent", None), ("after", None)] + assert tracer.get_current_step() is None + + def test_async_early_break_releases_context(self, published: List[Any]) -> None: + @tracer.trace_async() + async def stream() -> AsyncGenerator[int, None]: + for i in range(3): + yield i + + @tracer.trace_async() + async def after() -> int: + return 1 + + async def main() -> None: + async for _ in stream(): + break + await after() + assert tracer.get_current_trace() is None + + asyncio.run(main()) + + assert published == [("after", None)] + + @pytest.mark.parametrize("finish", ["aclose", "exhaust"]) + def test_async_child_finished_after_parent_returns(self, published: List[Any], finish: str) -> None: + holder: Dict[str, Any] = {} + + @tracer.trace_async() + async def child() -> AsyncGenerator[int, None]: + for i in range(3): + yield i + + @tracer.trace_async() + async def parent() -> None: + gen = child() + await gen.__anext__() + holder["gen"] = gen + + @tracer.trace_async() + async def after() -> int: + return 1 + + async def main() -> None: + await parent() + if finish == "aclose": + await holder["gen"].aclose() + else: + async for _ in holder["gen"]: + pass + await after() + assert tracer.get_current_step() is None + + asyncio.run(main()) + + assert published == [("parent", None), ("after", None)] + + def test_async_stream_finished_in_another_task_publishes_to_its_own_pipeline( + self, published: List[Any] + ) -> None: + holder: Dict[str, Any] = {} + + @tracer.trace_async(inference_pipeline_id="pipeline-a") + async def stream() -> AsyncGenerator[int, None]: + for i in range(3): + yield i + + @tracer.trace_async(inference_pipeline_id="pipeline-b") + async def request_b() -> None: + async for _ in holder["gen"]: + pass + + async def start() -> None: + holder["gen"] = stream() + await holder["gen"].__anext__() + + async def main() -> None: + await asyncio.ensure_future(start()) + await asyncio.ensure_future(request_b()) + + asyncio.run(main()) + + assert published == [("stream", "pipeline-a"), ("request_b", "pipeline-b")] + + def test_sync_close_releases_context_without_publishing(self, published: List[Any]) -> None: + """close() can come from CPython (a `yield from` delegate, even in GC), so it never publishes.""" + finalized: List[bool] = [] + + @tracer.trace() + def stream() -> Generator[int, None, None]: + try: + yield from range(3) + finally: + finalized.append(True) + + with contextlib.closing(stream()) as gen: + for _ in gen: + break + + assert finalized == [True] + assert published == [] + assert tracer.get_current_step() is None + assert tracer.get_current_trace() is None + + def test_dropped_streams_never_publish(self) -> None: + """__del__ can run inside GC, on a thread holding executor or HTTP-pool locks.""" + + @tracer.trace() + def stream() -> Generator[int, None, None]: + yield from range(3) + + @tracer.trace_async() + async def astream() -> AsyncGenerator[int, None]: + for i in range(3): + yield i + + async def drop_async() -> None: + gen = astream() + await gen.__anext__() + del gen + gc.collect() + assert tracer.get_current_step() is None + + gc.collect() # leftover garbage from earlier tests must not hit the mock + with patch.object(tracer, "_handle_trace_completion") as completion: + gen = stream() + next(gen) + del gen + gc.collect() + asyncio.run(drop_async()) + + completion.assert_not_called() + assert tracer.get_current_step() is None + assert tracer.get_current_trace() is None + + def test_root_stream_finishing_before_a_later_stream_releases_context( + self, published: List[Any] + ) -> None: + @tracer.trace() + def short() -> Generator[int, None, None]: + yield from range(2) + + @tracer.trace() + def long() -> Generator[int, None, None]: + yield from range(5) + + @tracer.trace() + def after() -> int: + return 1 + + for _ in zip(short(), long()): + pass + after() + + assert published == [("short", None), ("after", None)] + assert tracer.get_current_step() is None + + def test_async_cancelled_stream_publishes_and_releases_context(self) -> None: + published: List[Any] = [] + in_task: List[Any] = [] + + def capture(trace: traces.Trace, *args: Any) -> None: + published.append((trace.steps[0].name, trace.steps[0].metadata.get("Exceptions"))) + + @tracer.trace_async() + async def stream() -> AsyncGenerator[int, None]: + for i in range(3): + await asyncio.sleep(0.05) + yield i + + @tracer.trace_async() + async def after() -> int: + return 1 + + async def consume() -> None: + try: + async for _ in stream(): + pass + except asyncio.CancelledError: + in_task.append(tracer.get_current_step()) + await after() + raise + + async def main() -> None: + task = asyncio.ensure_future(consume()) + await asyncio.sleep(0.08) + task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await task + + with patch.object(tracer, "_publish", True), patch.object( + tracer, "_upload_and_publish_trace", side_effect=capture + ), patch.dict(tracer._tracer_config, {"background_publish_enabled": False}): + asyncio.run(main()) + + assert in_task == [None] + assert published == [("stream", "CancelledError"), ("after", None)] + + def test_yield_from_delegate_collected_by_gc_never_publishes(self) -> None: + """CPython closes a `yield from` delegate during GC; publishing there can deadlock.""" + + @tracer.trace() + def stream() -> Generator[int, None, None]: + yield from range(3) + + class Holder: + gen: Any = None + + def generate(self) -> Generator[int, None, None]: + yield from stream() + + gc.collect() # leftover garbage from earlier tests must not hit the mock + with patch.object(tracer, "_handle_trace_completion") as completion: + holder = Holder() + holder.gen = holder.generate() # holder -> generator frame -> holder: a cycle + next(holder.gen) + del holder + gc.collect() + + completion.assert_not_called() + assert tracer.get_current_step() is None + assert tracer.get_current_trace() is None + + def test_destroyed_pending_anext_never_publishes(self) -> None: + """A consumer coroutine destroyed mid-__anext__ is abandoned, not finished. + + Only discriminating on Python 3.13+: earlier versions leave the inner generator + ag_running, so the concurrent-consumer branch also avoids publishing. + """ + + class Pending: + def __await__(self) -> Generator[str, None, None]: + yield "pending" + + @tracer.trace_async() + async def stream() -> AsyncGenerator[int, None]: + yield 1 + await Pending() + yield 2 + + async def consume() -> None: + async for _ in stream(): + pass + + with patch.object(tracer, "_handle_trace_completion") as completion: + coro = consume() + assert coro.send(None) == "pending" + del coro + + completion.assert_not_called() + assert tracer.get_current_step() is None + + def test_non_root_stream_finishing_first_keeps_later_steps_under_parent(self) -> None: + shapes: List[Any] = [] + + def shape(step: steps.Step) -> Any: + nested: List[steps.Step] = step.steps + return (step.name, tuple(shape(s) for s in nested)) + + def capture(trace: traces.Trace, *args: Any) -> None: + shapes.append(shape(trace.steps[0])) + + @tracer.trace() + def short() -> Generator[int, None, None]: + yield from range(2) + + @tracer.trace() + def long() -> Generator[int, None, None]: + yield from range(5) + + @tracer.trace() + def after() -> int: + return 1 + + @tracer.trace() + def handler() -> None: + for _ in zip(short(), long()): + pass + after() + + with patch.object(tracer, "_publish", True), patch.object( + tracer, "_upload_and_publish_trace", side_effect=capture + ), patch.dict(tracer._tracer_config, {"background_publish_enabled": False}): + handler() + + assert shapes == [("handler", (("short", (("long", ()),)), ("after", ())))] + assert tracer.get_current_step() is None + + def test_close_before_start_runs_nothing(self, published: List[Any]) -> None: + started: List[bool] = [] + + @tracer.trace() + def stream() -> Generator[int, None, None]: + started.append(True) + yield 1 + + gen = stream() + gen.close() + + assert list(gen) == [] + assert started == [] + assert published == [] + + def test_concurrent_anext_from_another_task_does_not_end_the_stream(self, published: List[Any]) -> None: + holder: Dict[str, Any] = {} + + @tracer.trace_async() + async def stream() -> AsyncGenerator[str, None]: + yield "a" + await holder["gate"].wait() + yield "b" + + @tracer.trace_async() + async def after() -> int: + return 1 + + async def main() -> None: + holder["gate"] = asyncio.Event() + gen = stream() + assert await gen.__anext__() == "a" # this task starts the stream + rest = asyncio.ensure_future(gen.__anext__()) # another task continues it + await asyncio.sleep(0) + with pytest.raises(RuntimeError, match="already running"): + await gen.__anext__() + assert tracer.get_current_step() is None + assert published == [] + holder["gate"].set() + assert await rest == "b" + with pytest.raises(StopAsyncIteration): + await gen.__anext__() + await after() + + asyncio.run(main()) + + assert published == [("stream", None), ("after", None)] + + def test_close_while_running_keeps_the_stream_current(self, published: List[Any]) -> None: + holder: Dict[str, Any] = {} + + @tracer.trace() + def stream() -> Generator[int, None, None]: + yield 1 + with pytest.raises(ValueError, match="already executing"): + holder["gen"].close() + holder["current"] = tracer.get_current_step() + yield 2 + + holder["gen"] = stream() + assert list(holder["gen"]) == [1, 2] + assert holder["current"] is not None and holder["current"].name == "stream" + assert published == [("stream", None)] + + def test_aclose_before_start_runs_nothing(self, published: List[Any]) -> None: + started: List[bool] = [] + + @tracer.trace_async() + async def stream() -> AsyncGenerator[int, None]: + started.append(True) + yield 1 + + async def main() -> List[int]: + gen = stream() + await gen.aclose() + return [chunk async for chunk in gen] + + assert asyncio.run(main()) == [] + assert started == [] + assert published == [] + + def test_next_after_close_does_not_publish(self, published: List[Any]) -> None: + @tracer.trace() + def stream() -> Generator[int, None, None]: + yield from range(5) + + gen = stream() + for i in gen: + if i == 1: + gen.close() + + assert published == [] + assert tracer.get_current_step() is None + + def test_abandoned_stream_cleanup_stays_on_the_stream_step(self) -> None: + shapes: List[Any] = [] + + def shape(step: steps.Step) -> Any: + nested: List[steps.Step] = step.steps + return (step.name, tuple(shape(s) for s in nested)) + + def capture(trace: traces.Trace, *args: Any) -> None: + shapes.append(shape(trace.steps[0])) + + @tracer.trace() + def cleanup() -> None: + return None + + @tracer.trace() + def answer() -> Generator[int, None, None]: + try: + yield from range(3) + finally: + cleanup() + + @tracer.trace() + def handler() -> None: + for _ in answer(): + break + + with patch.object(tracer, "_publish", True), patch.object( + tracer, "_upload_and_publish_trace", side_effect=capture + ), patch.dict(tracer._tracer_config, {"background_publish_enabled": False}): + handler() + + assert shapes == [("handler", (("answer", (("cleanup", ()),)),))] + + def test_aclose_publishes_partial_root_stream(self, published: List[Any]) -> None: + @tracer.trace_async() + async def stream() -> AsyncGenerator[int, None]: + for i in range(3): + yield i + + async def main() -> None: + gen = stream() + await gen.__anext__() + await gen.aclose() + assert tracer.get_current_step() is None + + asyncio.run(main()) + + assert published == [("stream", None)] + + def test_aclose_during_coroutine_teardown_never_publishes(self) -> None: + """An aclosing() exit in a destroyed (e.g. garbage-collected) coroutine is abandonment.""" + + class Pending: + def __await__(self) -> Generator[str, None, None]: + yield "pending" + + @tracer.trace_async() + async def stream() -> AsyncGenerator[int, None]: + for i in range(3): + yield i + + holder: Dict[str, Any] = {} + + async def consume() -> None: + holder["gen"] = gen = stream() + try: + await gen.__anext__() + await Pending() + finally: + await gen.aclose() + + async def drain(gen: Any) -> List[int]: + return [chunk async for chunk in gen] + + with patch.object(tracer, "_handle_trace_completion") as completion: + coro = consume() + assert coro.send(None) == "pending" + coro.close() + assert asyncio.run(drain(holder["gen"])) == [] # abandoned means ended + + completion.assert_not_called() + assert tracer.get_current_step() is None + + def test_aclose_while_running_raises_without_ending_the_stream(self, published: List[Any]) -> None: + holder: Dict[str, Any] = {} + + @tracer.trace_async() + async def stream() -> AsyncGenerator[str, None]: + yield "a" + await holder["gate"].wait() + yield "b" + + async def main() -> None: + holder["gate"] = asyncio.Event() + gen = stream() + await gen.__anext__() + rest = asyncio.ensure_future(gen.__anext__()) + await asyncio.sleep(0) + with pytest.raises(RuntimeError, match="already running"): + await gen.aclose() + assert published == [] + holder["gate"].set() + assert await rest == "b" + assert [chunk async for chunk in gen] == [] + + asyncio.run(main()) + + assert published == [("stream", None)] + + def test_concurrent_next_from_another_thread_does_not_end_the_stream( + self, published: List[Any] + ) -> None: + started, go = threading.Event(), threading.Event() + + @tracer.trace() + def stream() -> Generator[int, None, None]: + yield 1 + started.set() + go.wait(5) + yield 2 + + gen = stream() + assert next(gen) == 1 + got: List[int] = [] + worker = threading.Thread(target=lambda: got.append(next(gen))) + worker.start() + try: + assert started.wait(5) + with pytest.raises(ValueError, match="already executing"): + next(gen) + assert tracer.get_current_step() is None + assert published == [] + finally: + go.set() + worker.join(5) + + assert got == [2] + assert list(gen) == [] + assert published == [("stream", None)] + + def test_open_function_step_keeps_children_after_draining_a_nested_stream(self) -> None: + shapes: List[Any] = [] + + def shape(step: steps.Step) -> Any: + nested: List[steps.Step] = step.steps + return (step.name, tuple(shape(s) for s in nested)) + + def capture(trace: traces.Trace, *args: Any) -> None: + shapes.append(shape(trace.steps[0])) + + @tracer.trace() + def child() -> Generator[int, None, None]: + yield from range(2) + + @tracer.trace() + def validate() -> int: + return 1 + + @tracer.trace() + def drain(stream: Any) -> None: + list(stream) + validate() + + @tracer.trace() + def handler() -> None: + stream = child() + next(stream) + drain(stream) + + with patch.object(tracer, "_publish", True), patch.object( + tracer, "_upload_and_publish_trace", side_effect=capture + ), patch.dict(tracer._tracer_config, {"background_publish_enabled": False}): + handler() + + assert shapes == [("handler", (("child", (("drain", (("validate", ()),)),)),))] + + def test_root_stream_finishing_inside_a_consumer_releases_its_trace(self, published: List[Any]) -> None: + @tracer.trace() + def stream() -> Generator[int, None, None]: + yield from range(2) + + @tracer.trace() + def validate() -> int: + return 1 + + @tracer.trace() + def consumer(gen: Any) -> None: + list(gen) + validate() + + gen = stream() + next(gen) + consumer(gen) + + # validate() starts its own trace instead of vanishing into the finished one. + assert published == [("stream", None), ("validate", None)] + + def test_abandoned_stream_body_is_finalized_once(self) -> None: + exits: List[bool] = [] + + def ignore_unraisable(unraisable: Any) -> None: + # pytest's hook would keep the reported exception, and with it the generator. + pass + + @tracer.trace() + def stream() -> Generator[int, None, None]: + while True: # a retry loop that swallows GeneratorExit + try: + yield 1 + except GeneratorExit: + exits.append(True) + if len(exits) > 2: + return + + with patch.object(sys, "unraisablehook", ignore_unraisable): + gen = stream() + next(gen) + del gen + gc.collect() + + assert exits == [True] + assert tracer.get_current_step() is None + + def test_stream_abandoned_in_another_context_releases_the_one_that_iterates_on( + self, published: List[Any] + ) -> None: + class Pending: + def __await__(self) -> Generator[str, None, None]: + yield "pending" + + @tracer.trace_async() + async def stream() -> AsyncGenerator[int, None]: + yield 1 + await Pending() + yield 2 + + @tracer.trace_async() + async def after() -> int: + return 1 + + async def main() -> None: + gen = stream() + assert await gen.__anext__() == 1 # this context starts the stream + other = contextvars.copy_context() # e.g. another task's context + pending = gen.__anext__() + assert other.run(pending.send, None) == "pending" + other.run(pending.close) # that coroutine is destroyed: the stream is abandoned + assert [chunk async for chunk in gen] == [] + assert tracer.get_current_step() is None + await after() + + asyncio.run(main()) + + assert published == [("after", None)]