Skip to content

Conversation

dhruv-ahuja
Copy link
Contributor

@dhruv-ahuja dhruv-ahuja commented Sep 25, 2025

Addresses the concerns mentioned in #1424, ensuring the span calls raise runtime warnings with apt documentation link, when used inside generator functions.

Test script output:

uvp spanner.py                                      
/Users/dhruv/coding/python/projects/oss/logfire/logfire/_internal/main.py:204: RuntimeWarning: Span is inside a generator function. See https://logfire.pydantic.dev/docs/reference/advanced/generators/#move-the-span-outside-the-generator.
  warnings.warn(
21:10:07.772 gen3
             │ spanner.py:28
21:10:07.773   usage within gen3 (manual span)
             │ spanner.py:29 info
/Users/dhruv/coding/python/projects/oss/logfire/logfire/_internal/main.py:204: RuntimeWarning: Span is inside a generator function. See https://logfire.pydantic.dev/docs/reference/advanced/generators/#move-the-span-outside-the-generator.
  warnings.warn(
21:10:07.774 async_gen
             │ spanner.py:41
21:10:07.774   usage within async_gen (manual span)
             │ spanner.py:42 info
Failed to detach context
Traceback (most recent call last):
  File "/Users/dhruv/coding/python/projects/oss/logfire/spanner.py", line 43, in async_gen
    yield
GeneratorExit

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/dhruv/coding/python/projects/oss/logfire/.venv/lib/python3.13/site-packages/opentelemetry/context/__init__.py", line 155, in detach
    _RUNTIME_CONTEXT.detach(token)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/dhruv/coding/python/projects/oss/logfire/.venv/lib/python3.13/site-packages/opentelemetry/context/contextvars_context.py", line 53, in detach
    self._current_context.reset(token)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
ValueError: <Token var=<ContextVar name='current_context' default={} at 0x104a39170> at 0x10746adc0> was created in a different Context
Exception ignored in: <async_generator object async_gen at 0x1073729b0>
Traceback (most recent call last):
  File "/Users/dhruv/coding/python/projects/oss/logfire/spanner.py", line 49, in <module>
    asyncio.run(anext(async_gen()))
StopAsyncIteration: 

@dhruv-ahuja dhruv-ahuja force-pushed the enhance/warn_on_span_yield branch from e52430e to 16488d2 Compare October 3, 2025 14:40
Copy link

codecov bot commented Oct 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@dhruv-ahuja
Copy link
Contributor Author

@alexmojaki please review this code when convenient

Copy link
Contributor

@alexmojaki alexmojaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add to the docs

_span_name: str | None = None,
_level: LevelName | int | None = None,
_links: Sequence[tuple[SpanContext, otel_types.Attributes]] = (),
_warn_if_inside_generator: bool = True,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_warn_if_inside_generator: bool = True,
_allow_generator: bool = False,

to match logfire.instrument


is_from_context_manager = False

# Check if this call is coming from inside a context manager generator by inspecting call stack frames
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this logic is generally correct. But where is it being tested anyway?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here's an example of this missing bad usage:

import logfire

logfire.configure()


class MyContextManager:
    def __enter__(self):
        self.span = logfire.span('MyContextManager')  # correct usage
        items = bad()
        for _ in items:
            break
        logfire.info('after bad')  # incorrectly appears under 'bad'
        self.span.__enter__()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.span.__exit__(exc_type, exc_value, traceback)
        return True


def bad():
    with logfire.span('bad'):  # incorrect usage
        for x in range(3):
            yield x


with MyContextManager():
    logfire.info('Inside context manager')  # incorrectly does not appear under 'MyContextManager'

I suggest leaving this for now, it's OK for the user to get some redundant warnings. But it might be possible to follow up with improvement detecting the contextlib decorators.

if caller_is_generator and _warn_if_inside_generator and not is_from_context_manager:
warnings.warn(
'Span is inside a generator function. See https://logfire.pydantic.dev/docs/reference/advanced/generators/#move-the-span-outside-the-generator.',
RuntimeWarning,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be a UserWarning

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants