Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions logfire/_internal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
from typing import TextIO

from logfire.variables import VariablesConfig
from logfire.variables.variable import TemplateVariable, Variable

from .main import Logfire

Expand Down Expand Up @@ -1019,6 +1020,7 @@ def __init__(
self._variable_provider: VariableProvider = NoOpVariableProvider()
self._logger_provider = ProxyLoggerProvider(NoOpLoggerProvider())
self._otlp_forwarding = OTLPForwardingManager([])
self._variables: dict[str, Variable[Any] | TemplateVariable[Any, Any]] = {}
# This ensures that we only call OTEL's global set_tracer_provider once to avoid warnings.
self._has_set_providers = False
self._initialized = False
Expand Down
16 changes: 10 additions & 6 deletions logfire/_internal/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,15 @@ def __init__(
self._sample_rate = sample_rate
self._console_log = console_log
self._otel_scope = otel_scope
self._variables: dict[str, Variable[Any] | TemplateVariable[Any, Any]] = {}

@property
def config(self) -> LogfireConfig:
return self._config

@property
def _variables(self) -> dict[str, Variable[Any] | TemplateVariable[Any, Any]]:
return self._config._variables # pyright: ignore[reportPrivateUsage]

@property
def resource_attributes(self) -> Mapping[str, Any]:
return self._tracer_provider.resource.attributes
Expand Down Expand Up @@ -2748,17 +2751,18 @@ class PromptInputs(BaseModel):
return variable

def variables_clear(self) -> None:
"""Clear all registered variables from this Logfire instance.
"""Clear all variables registered with this Logfire instance's config.

This removes all variables previously registered via [`var()`][logfire.Logfire.var]
or [`template_var()`][logfire.Logfire.template_var],
allowing them to be re-registered. This is primarily intended for use in tests
to ensure a clean state between test cases.
or [`template_var()`][logfire.Logfire.template_var] on this instance or any
[`with_settings()`][logfire.Logfire.with_settings] sibling that shares its config,
allowing them to be re-registered. This is primarily intended for use in tests to
ensure a clean state between test cases.
"""
self._variables.clear()

def variables_get(self) -> list[Variable[Any] | TemplateVariable[Any, Any]]:
"""Get all variables registered with this Logfire instance."""
"""Get all variables registered with this Logfire instance's config."""
return list(self._variables.values())

def variables_push(
Expand Down
42 changes: 42 additions & 0 deletions tests/test_push_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,48 @@ def test_get_variables_returns_all_registered() -> None:
assert var3 in variables


def test_with_settings_shares_registered_variables() -> None:
"""Variables registered on with_settings() siblings share one config registry."""
from logfire._internal.main import Logfire

lf = Logfire()
lf2 = lf.with_settings(tags=['other'])

var1 = lf.var(name='feature_a', default=False, type=bool)
var2 = lf2.var(name='feature_b', default='hello', type=str)

assert lf.variables_get() == [var1, var2]
assert lf2.variables_get() == [var1, var2]


def test_with_settings_duplicate_variable_names_conflict() -> None:
"""Duplicate variable names are rejected across with_settings() siblings."""
from logfire._internal.main import Logfire

lf = Logfire()
lf2 = lf.with_settings(tags=['other'])

lf.var(name='feature_enabled', default=False, type=bool)
with pytest.raises(ValueError, match="A variable with name 'feature_enabled' has already been registered"):
lf2.var(name='feature_enabled', default=True, type=bool)


def test_variables_clear_clears_with_settings_siblings() -> None:
"""variables_clear() clears the shared config registry."""
from logfire._internal.main import Logfire

lf = Logfire()
lf2 = lf.with_settings(tags=['other'])

lf.var(name='feature_a', default=False, type=bool)
lf2.var(name='feature_b', default='hello', type=str)

lf.variables_clear()

assert lf.variables_get() == []
assert lf2.variables_get() == []
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated


# --- Validation tests ---


Expand Down
15 changes: 15 additions & 0 deletions tests/test_variable_composition.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,21 @@ def test_simple_reference(self, config_kwargs: dict[str, Any]):
assert result.composed_from[0].name == 'greeting'
assert result.composed_from[0].value == 'Hello'

def test_code_default_reference_across_with_settings_siblings(self, config_kwargs: dict[str, Any]):
"""Code-default composition sees variables registered on with_settings() siblings."""
lf = logfire.configure(**config_kwargs)
lf2 = lf.with_settings(tags=['other'])

lf.var(name='greeting', default='Hello', type=str)
main = lf2.var(name='main', default='@{greeting}@ World', type=str)

result = main.get()

assert result.value == 'Hello World'
assert len(result.composed_from) == 1
assert result.composed_from[0].name == 'greeting'
assert result.composed_from[0].value == 'Hello'

def test_override_participates_in_composition(self, config_kwargs: dict[str, Any]):
"""An override value containing @{ref}@ is expanded against the live config.

Expand Down
Loading