Skip to content

Commit 0787eac

Browse files
authored
Suppress LogfireNotConfiguredWarning from pydantic_graph and pydantic_evals (#2791)
1 parent 8149de4 commit 0787eac

File tree

5 files changed

+74
-20
lines changed

5 files changed

+74
-20
lines changed

pydantic_evals/pydantic_evals/_utils.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@
22

33
import asyncio
44
import inspect
5-
from collections.abc import Awaitable, Callable, Sequence
5+
import warnings
6+
from collections.abc import Awaitable, Callable, Generator, Sequence
7+
from contextlib import contextmanager
68
from functools import partial
7-
from typing import Any, TypeVar
9+
from pathlib import Path
10+
from typing import TYPE_CHECKING, Any, TypeVar
811

912
import anyio
13+
import logfire_api
1014
from typing_extensions import ParamSpec, TypeIs
1115

16+
_logfire = logfire_api.Logfire(otel_scope='pydantic-evals')
17+
logfire_api.add_non_user_code_prefix(Path(__file__).parent.absolute())
18+
1219

1320
class Unset:
1421
"""A singleton to represent an unset value.
@@ -101,3 +108,28 @@ async def _run_task(tsk: Callable[[], Awaitable[T]], index: int) -> None:
101108
tg.start_soon(_run_task, task, i)
102109

103110
return results
111+
112+
113+
try:
114+
from logfire._internal.config import (
115+
LogfireNotConfiguredWarning, # pyright: ignore[reportAssignmentType,reportPrivateImportUsage]
116+
)
117+
# TODO: Remove this `pragma: no cover` once we test evals without pydantic-ai (which includes logfire)
118+
except ImportError: # pragma: no cover
119+
120+
class LogfireNotConfiguredWarning(UserWarning):
121+
pass
122+
123+
124+
if TYPE_CHECKING:
125+
logfire_span = _logfire.span
126+
else:
127+
128+
@contextmanager
129+
def logfire_span(*args: Any, **kwargs: Any) -> Generator[logfire_api.LogfireSpan, None, None]:
130+
"""Create a Logfire span without warning if logfire is not configured."""
131+
# TODO: Remove once Logfire has the ability to suppress this warning from non-user code
132+
with warnings.catch_warnings():
133+
warnings.filterwarnings('ignore', category=LogfireNotConfiguredWarning)
134+
with _logfire.span(*args, **kwargs) as span:
135+
yield span

pydantic_evals/pydantic_evals/dataset.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
from pydantic_evals._utils import get_event_loop
3838

39-
from ._utils import get_unwrapped_function_name, task_group_gather
39+
from ._utils import get_unwrapped_function_name, logfire_span, task_group_gather
4040
from .evaluators import EvaluationResult, Evaluator
4141
from .evaluators._run_evaluator import run_evaluator
4242
from .evaluators.common import DEFAULT_EVALUATORS
@@ -283,7 +283,7 @@ async def evaluate(
283283
limiter = anyio.Semaphore(max_concurrency) if max_concurrency is not None else AsyncExitStack()
284284

285285
with (
286-
_logfire.span('evaluate {name}', name=name, n_cases=len(self.cases)) as eval_span,
286+
logfire_span('evaluate {name}', name=name, n_cases=len(self.cases)) as eval_span,
287287
progress_bar or nullcontext(),
288288
):
289289
task_id = progress_bar.add_task(f'Evaluating {name}', total=total_cases) if progress_bar else None
@@ -858,7 +858,7 @@ async def _run_once():
858858
token = _CURRENT_TASK_RUN.set(task_run_)
859859
try:
860860
with (
861-
_logfire.span('execute {task}', task=get_unwrapped_function_name(task)) as task_span,
861+
logfire_span('execute {task}', task=get_unwrapped_function_name(task)) as task_span,
862862
context_subtree() as span_tree_,
863863
):
864864
t0 = time.perf_counter()
@@ -933,7 +933,7 @@ async def _run_task_and_evaluators(
933933
trace_id: str | None = None
934934
span_id: str | None = None
935935
try:
936-
with _logfire.span(
936+
with logfire_span(
937937
'case: {case_name}',
938938
task_name=get_unwrapped_function_name(task),
939939
case_name=report_case_name,

pydantic_evals/pydantic_evals/evaluators/_run_evaluator.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
import traceback
44
from collections.abc import Mapping
5-
from pathlib import Path
65
from typing import TYPE_CHECKING, Any
76

8-
import logfire_api
97
from pydantic import (
108
TypeAdapter,
119
ValidationError,
1210
)
1311
from typing_extensions import TypeVar
1412

13+
from pydantic_evals._utils import logfire_span
14+
1515
from .context import EvaluatorContext
1616
from .evaluator import (
1717
EvaluationReason,
@@ -25,8 +25,6 @@
2525
if TYPE_CHECKING:
2626
from pydantic_ai.retries import RetryConfig
2727

28-
_logfire = logfire_api.Logfire(otel_scope='pydantic-evals')
29-
logfire_api.add_non_user_code_prefix(Path(__file__).parent.absolute())
3028

3129
InputsT = TypeVar('InputsT', default=Any, contravariant=True)
3230
OutputT = TypeVar('OutputT', default=Any, contravariant=True)
@@ -62,7 +60,7 @@ async def run_evaluator(
6260
evaluate = tenacity_retry(**retry)(evaluate)
6361

6462
try:
65-
with _logfire.span(
63+
with logfire_span(
6664
'evaluator: {evaluator_name}',
6765
evaluator_name=evaluator.get_default_evaluation_name(),
6866
):

pydantic_graph/pydantic_graph/_utils.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@
22

33
import asyncio
44
import types
5-
from collections.abc import Callable
5+
import warnings
6+
from collections.abc import Callable, Generator
7+
from contextlib import contextmanager
68
from functools import partial
79
from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar, get_args, get_origin
810

9-
from logfire_api import LogfireSpan
11+
from logfire_api import Logfire, LogfireSpan
1012
from typing_extensions import ParamSpec, TypeIs
1113
from typing_inspection import typing_objects
1214
from typing_inspection.introspection import is_union_origin
1315

1416
if TYPE_CHECKING:
1517
from opentelemetry.trace import Span
1618

19+
_logfire = Logfire(otel_scope='pydantic-graph')
1720

1821
AbstractSpan: TypeAlias = 'LogfireSpan | Span'
1922

@@ -136,3 +139,27 @@ async def run_in_executor(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.k
136139
return await asyncio.get_running_loop().run_in_executor(None, partial(func, *args, **kwargs))
137140
else:
138141
return await asyncio.get_running_loop().run_in_executor(None, func, *args) # type: ignore
142+
143+
144+
try:
145+
from logfire._internal.config import (
146+
LogfireNotConfiguredWarning, # pyright: ignore[reportAssignmentType,reportPrivateImportUsage]
147+
)
148+
except ImportError:
149+
150+
class LogfireNotConfiguredWarning(UserWarning):
151+
pass
152+
153+
154+
if TYPE_CHECKING:
155+
logfire_span = _logfire.span
156+
else:
157+
158+
@contextmanager
159+
def logfire_span(*args: Any, **kwargs: Any) -> Generator[LogfireSpan, None, None]:
160+
"""Create a Logfire span without warning if logfire is not configured."""
161+
# TODO: Remove once Logfire has the ability to suppress this warning from non-user code
162+
with warnings.catch_warnings():
163+
warnings.filterwarnings('ignore', category=LogfireNotConfiguredWarning)
164+
with _logfire.span(*args, **kwargs) as span:
165+
yield span

pydantic_graph/pydantic_graph/graph.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,17 @@
99
from pathlib import Path
1010
from typing import Any, Generic, cast, overload
1111

12-
import logfire_api
1312
import typing_extensions
1413
from typing_inspection import typing_objects
1514

1615
from . import _utils, exceptions, mermaid
17-
from ._utils import AbstractSpan, get_traceparent
16+
from ._utils import AbstractSpan, get_traceparent, logfire_span
1817
from .nodes import BaseNode, DepsT, End, GraphRunContext, NodeDef, RunEndT, StateT
1918
from .persistence import BaseStatePersistence
2019
from .persistence.in_mem import SimpleStatePersistence
2120

2221
__all__ = 'Graph', 'GraphRun', 'GraphRunResult'
2322

24-
_logfire = logfire_api.Logfire(otel_scope='pydantic-graph')
25-
2623

2724
@dataclass(init=False)
2825
class Graph(Generic[StateT, DepsT, RunEndT]):
@@ -242,7 +239,7 @@ async def iter(
242239
entered_span: AbstractSpan | None = None
243240
if span is None:
244241
if self.auto_instrument:
245-
entered_span = stack.enter_context(logfire_api.span('run graph {graph.name}', graph=self))
242+
entered_span = stack.enter_context(logfire_span('run graph {graph.name}', graph=self))
246243
else:
247244
entered_span = stack.enter_context(span)
248245
traceparent = None if entered_span is None else get_traceparent(entered_span)
@@ -291,7 +288,7 @@ async def iter_from_persistence(
291288
snapshot.node.set_snapshot_id(snapshot.id)
292289

293290
if self.auto_instrument and span is None: # pragma: no branch
294-
span = logfire_api.span('run graph {graph.name}', graph=self)
291+
span = logfire_span('run graph {graph.name}', graph=self)
295292

296293
with ExitStack() as stack:
297294
entered_span = None if span is None else stack.enter_context(span)
@@ -727,7 +724,7 @@ async def main():
727724

728725
with ExitStack() as stack:
729726
if self.graph.auto_instrument:
730-
stack.enter_context(_logfire.span('run node {node_id}', node_id=node_id, node=node))
727+
stack.enter_context(logfire_span('run node {node_id}', node_id=node_id, node=node))
731728

732729
async with self.persistence.record_run(node_snapshot_id):
733730
ctx = GraphRunContext(state=self.state, deps=self.deps)

0 commit comments

Comments
 (0)