Skip to content

Commit 5adee0c

Browse files
committed
feat(otel): align resolve_feature_flags with strict LLD requirements
1 parent e3c8cb8 commit 5adee0c

3 files changed

Lines changed: 50 additions & 43 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
from .options import (
22
clear_test_env_overrides,
3-
is_signal_enabled,
3+
resolve_feature_flags,
44
set_test_env_override,
55
)
66

77
__all__ = [
8-
"is_signal_enabled",
8+
"resolve_feature_flags",
99
"set_test_env_override",
1010
"clear_test_env_overrides",
1111
]

packages/google-api-core/google/api_core/observability/options.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -74,55 +74,60 @@ def _get_env_bool(name: str) -> Optional[bool]:
7474

7575
def _get_env_bool_with_dev_fallback(name: str) -> Optional[bool]:
7676
"""Retrieve the boolean value of an environment variable, checking experimental fallbacks first."""
77-
if name.startswith("GOOGLE_CLOUD_"):
78-
exp_name = name.replace("GOOGLE_CLOUD_", "GOOGLE_CLOUD_EXPERIMENTAL_", 1)
77+
if name.startswith("GOOGLE_SDK_"):
78+
exp_name = name.replace("GOOGLE_SDK_", "GOOGLE_SDK_EXPERIMENTAL_", 1)
7979
val = _get_env_bool(exp_name)
8080
if val is not None:
8181
return val
8282
return _get_env_bool(name)
8383

8484

85-
def is_signal_enabled(
86-
signal_type: str,
85+
def resolve_feature_flags(
86+
feature_name: str,
8787
client_options: Optional[Union[Dict[str, Any], Any]] = None,
8888
default: bool = False,
8989
) -> bool:
9090
"""Determines if a telemetry signal is enabled.
9191
9292
Resolves settings in the following order of precedence:
9393
1. Programmatic overrides in client_options (checks tracer_provider)
94-
2. Language-wide Environment Variable: GOOGLE_CLOUD_PYTHON_TRACING_ENABLED
94+
2. Language-wide Environment Variable: GOOGLE_SDK_PYTHON_TRACING_ENABLED
9595
(natively checks for a variant with an "EXPERIMENTAL" token first)
9696
3. Default fallback
9797
9898
Args:
99-
signal_type: The signal type: must be 'tracing'.
99+
feature_name: The feature name: must be 'tracing'.
100100
client_options: A dictionary or object containing client configuration.
101101
default: Fallback boolean if no options or env variables match.
102102
103103
Returns:
104104
bool: True if the signal is resolved to enabled, False otherwise.
105105
"""
106-
if signal_type != "tracing":
106+
if feature_name != "tracing":
107107
raise ValueError(
108-
f"Invalid signal_type: {signal_type!r}. Only 'tracing' is supported at this time."
108+
f"Invalid feature_name: {feature_name!r}. Only 'tracing' is supported at this time."
109109
)
110110

111-
# 1. Resolve Programmatic Options First
111+
# 1. Experimental Gate
112+
exp_var = "GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED"
113+
exp_val = _get_env_bool(exp_var)
114+
115+
has_provider = False
112116
if client_options is not None:
113117
options_dict = (
114118
client_options
115119
if isinstance(client_options, dict)
116120
else getattr(client_options, "__dict__", {})
117121
)
118-
119122
if options_dict.get("tracer_provider") is not None:
120-
return True
123+
has_provider = True
121124

122-
# 2. Check Language-Wide Environment Variable
123-
val = _get_env_bool_with_dev_fallback("GOOGLE_CLOUD_PYTHON_TRACING_ENABLED")
124-
if val is not None:
125-
return val
125+
if exp_val is not True:
126+
if has_provider:
127+
raise ValueError(
128+
f"Experimental feature {feature_name!r} requires {exp_var} to be set to 'true' to use programmatic providers."
129+
)
130+
return False # Blocked
126131

127-
# 3. Default Fallback
128-
return default
132+
# If we are here, exp_val IS True.
133+
return True

packages/google-api-core/tests/unit/observability/test_options.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# limitations under the License.
1414

1515
import pytest
16-
1716
from google.api_core.observability import options
1817
from google.api_core.observability.options import (
1918
_get_env_bool,
@@ -104,68 +103,71 @@ def test_get_env_bool_with_dev_fallback_other_prefix(monkeypatch):
104103

105104

106105
@pytest.mark.parametrize(
107-
"signal_type, env_vars, client_options, default_val, expected",
106+
"feature_name, env_vars, client_options, default_val, expected",
108107
[
109108
# Default fallback tests
110109
("tracing", {}, None, False, False),
111-
("tracing", {}, None, True, True),
112-
# Global env var
113-
("tracing", {"GOOGLE_CLOUD_PYTHON_TRACING_ENABLED": True}, None, False, True),
114-
("tracing", {"GOOGLE_CLOUD_PYTHON_TRACING_ENABLED": False}, None, True, False),
110+
# Default=True is ignored if blocked
111+
("tracing", {}, None, True, False),
112+
# Global GA env var is ignored if experimental flag is missing
113+
("tracing", {"GOOGLE_SDK_PYTHON_TRACING_ENABLED": True}, None, False, False),
114+
("tracing", {"GOOGLE_SDK_PYTHON_TRACING_ENABLED": False}, None, True, False),
115115
# Experimental fallback
116116
(
117117
"tracing",
118-
{"GOOGLE_CLOUD_EXPERIMENTAL_PYTHON_TRACING_ENABLED": True},
118+
{"GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED": True},
119119
None,
120120
False,
121121
True,
122122
),
123123
(
124124
"tracing",
125-
{"GOOGLE_CLOUD_EXPERIMENTAL_PYTHON_TRACING_ENABLED": False},
125+
{"GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED": False},
126126
None,
127127
True,
128128
False,
129129
),
130-
# Implicit opt-in with provider
131-
(
132-
"tracing",
133-
{"GOOGLE_CLOUD_PYTHON_TRACING_ENABLED": False},
134-
{"tracer_provider": object()},
135-
False,
136-
True,
137-
),
138130
# Programmatic boolean flags are NOT supported in client_options
139131
# (should default/fallback to False)
140132
(
141133
"tracing",
142-
{"GOOGLE_CLOUD_PYTHON_TRACING_ENABLED": False},
134+
{"GOOGLE_SDK_PYTHON_TRACING_ENABLED": False},
143135
{"enable_metrics": True},
144136
False,
145137
False,
146138
),
147139
(
148140
"tracing",
149-
{"GOOGLE_CLOUD_PYTHON_TRACING_ENABLED": False},
141+
{"GOOGLE_SDK_PYTHON_TRACING_ENABLED": False},
150142
{"enable_tracing": True},
151143
False,
152144
False,
153145
),
154146
],
155147
)
156-
def test_is_signal_enabled(
157-
signal_type, env_vars, client_options, default_val, expected
148+
def test_resolve_feature_flags(
149+
feature_name, env_vars, client_options, default_val, expected
158150
):
159151
# Setup environment variables using our test overrides
160152
for k, v in env_vars.items():
161153
set_test_env_override(k, v)
162154

163-
result = options.is_signal_enabled(
164-
signal_type, client_options=client_options, default=default_val
155+
result = options.resolve_feature_flags(
156+
feature_name, client_options=client_options, default=default_val
165157
)
166158
assert result is expected
167159

168160

169-
def test_is_signal_enabled_invalid_signal():
161+
def test_resolve_feature_flags_invalid_signal():
170162
with pytest.raises(ValueError, match="Only 'tracing' is supported"):
171-
options.is_signal_enabled("metrics")
163+
options.resolve_feature_flags("metrics")
164+
165+
166+
def test_resolve_feature_flags_experimental_gate_blocks_provider():
167+
"""Verify that programmatic provider is blocked for experimental features without env var."""
168+
# Assume 'tracing' is experimental for this test.
169+
# We pass tracer_provider but do NOT set GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED
170+
client_options = {"tracer_provider": object()}
171+
172+
with pytest.raises(ValueError, match="Experimental feature"):
173+
options.resolve_feature_flags("tracing", client_options=client_options)

0 commit comments

Comments
 (0)