Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/api_core/_feature_gating_helpers.py: 28%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

46 statements  

1# -*- coding: utf-8 -*- 

2# Copyright 2026 Google LLC 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); 

5# you may not use this file except in compliance with the License. 

6# You may obtain a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, 

12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

13# See the License for the specific language governing permissions and 

14# limitations under the License. 

15# 

16 

17"""Observability environment variable and client options resolution helpers.""" 

18 

19import os 

20import warnings 

21from typing import Any 

22 

23# We import ClientOptions for type hinting 

24from google.api_core.client_options import ClientOptions 

25 

26# Allowed truthy and falsy patterns for environment variables 

27_TRUTHY_VALUES = ("y", "yes", "t", "true", "on", "1") 

28_FALSY_VALUES = ("n", "no", "f", "false", "off", "0") 

29 

30 

31class FeatureGatingError(ValueError): 

32 """Raised when feature gating resolution fails or is misconfigured.""" 

33 

34 pass 

35 

36 

37def _strtobool(val: str) -> bool | None: 

38 """Convert a string representation of truth to a boolean.""" 

39 clean_val = val.lower().strip() 

40 if not clean_val: 

41 return None 

42 if clean_val in _TRUTHY_VALUES: 

43 return True 

44 if clean_val in _FALSY_VALUES: 

45 return False 

46 raise ValueError(f"Invalid truth value: {val!r}") 

47 

48 

49def _get_env_bool(name: str) -> bool | None: 

50 """Retrieve the boolean value of an environment variable.""" 

51 val = os.getenv(name) 

52 if val is None: 

53 return None 

54 try: 

55 return _strtobool(val) 

56 except ValueError as e: 

57 warnings.warn(f"Ignored invalid value for {name}: {e}", RuntimeWarning) 

58 return None 

59 

60 

61def _has_feature_key( 

62 *, configuration: ClientOptions | dict[str, Any] | None, feature_key: str 

63) -> bool: 

64 """Checks if a specific feature key is present and not None in configuration.""" 

65 if configuration is None: 

66 return False 

67 

68 if feature_key.startswith("__"): 

69 return False 

70 

71 if isinstance(configuration, dict): 

72 return configuration.get(feature_key) is not None 

73 

74 return getattr(configuration, feature_key, None) is not None 

75 

76 

77def resolve_feature_flags( 

78 *, 

79 env_var: str, 

80 feature_key: str, 

81 configuration: ClientOptions | dict[str, Any] | None = None, 

82) -> bool: 

83 """Determines if a feature is enabled based on environment variables and configuration. 

84 

85 Behavior depends on whether the `env_var` name contains "EXPERIMENTAL": 

86 

87 - **Experimental Path** (env_var contains "EXPERIMENTAL"): 

88 Strict control. Requires the environment variable to be explicitly 'true'. 

89 If a programmatic feature key is passed but the environment variable is not 'true', 

90 raises FeatureGatingError (Fail Fast). 

91 

92 - **GA Path** (env_var does not contain "EXPERIMENTAL"): 

93 Standard precedence. Enabled if a programmatic feature key is passed, 

94 otherwise falls back to the environment variable value. 

95 

96 Args: 

97 env_var: The name of the environment variable controlling this feature. 

98 feature_key: The key in configuration/attributes for the programmatic configuration. 

99 configuration: Optional. A dictionary or object containing client configuration. 

100 

101 Returns: 

102 bool: True if the feature is resolved to enabled, False otherwise. 

103 

104 Raises: 

105 FeatureGatingError: If a feature key is provided for an experimental feature without enabling the experimental environment variable. 

106 """ 

107 

108 # Check for programmatic feature configuration 

109 has_feature_key = _has_feature_key( 

110 configuration=configuration, feature_key=feature_key 

111 ) 

112 

113 # Read environment variable 

114 env_var_setting = _get_env_bool(env_var) 

115 

116 # EXPERIMENTAL PATH: 

117 # Resolution Hierarchy: 

118 # 1. EXPERIMENTAL Environment Variable 

119 # 2. Fail Fast if Feature Key present but EXPERIMENTAL Environment Variable is not enabled 

120 if "EXPERIMENTAL" in env_var: 

121 # Fail Fast if feature key present but experimental environment variable is not enabled 

122 if env_var_setting is not True and has_feature_key: 

123 raise FeatureGatingError( 

124 f"Experimental feature requires {env_var} to be set to 'true' to use programmatic configuration." 

125 ) 

126 

127 return bool(env_var_setting) 

128 

129 # GENERAL AVAILABILITY PATH: 

130 # Resolution Hierarchy: 

131 # 1. Programmatic Configuration (Feature Key) 

132 # 2. Environment Variable 

133 

134 # Check Programmatic Configuration 

135 if has_feature_key: 

136 return True 

137 

138 # Check Environment Variable 

139 return bool(env_var_setting)