Skip to content

Commit ecbc418

Browse files
authored
App Configuration Provider Cleanup (#47801)
* Updating Provider Mostly docstring fixes and a couple bug fixes. * fixed formatting * pylint updates * undo skills * Update CHANGELOG.md * Moving bug fixes to there own PR * moving other changes to other branch * Update _utils.py * Update _utils.py * review comments * updated return description * api mds
1 parent 61acdbf commit ecbc418

21 files changed

Lines changed: 129 additions & 131 deletions
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
apiMdSha256: 1b252a78094b95bf515ebe5ec67fc2de12ce5b595bad25438a58241ca7153b84
2-
parserVersion: 0.3.28
2+
parserVersion: 0.3.30
33
pythonVersion: 3.14.3

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_azureappconfigurationprovider.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
from ._user_agent import USER_AGENT
3535
from ._utils import get_startup_backoff
3636

37-
JSON = Mapping[str, Any]
3837
logger = logging.getLogger(__name__)
3938

4039

@@ -96,7 +95,19 @@ def __init__(self, **kwargs: Any) -> None:
9695
self._on_refresh_error: Optional[Callable[[Exception], None]] = kwargs.pop("on_refresh_error", None)
9796
self._configuration_mapper: Optional[Callable] = kwargs.pop("configuration_mapper", None)
9897

99-
def _attempt_refresh(self, client: ConfigurationClient, replica_count: int, is_failover_request: bool, **kwargs):
98+
def _attempt_refresh(
99+
self, client: ConfigurationClient, replica_count: int, is_failover_request: bool, **kwargs
100+
) -> None:
101+
"""
102+
Attempts to refresh configuration settings and feature flags using a single client.
103+
104+
:param client: The configuration client to attempt the refresh against.
105+
:type client: ~azure.appconfiguration.provider.ConfigurationClient
106+
:param replica_count: The number of replica clients available, used for correlation telemetry.
107+
:type replica_count: int
108+
:param is_failover_request: Whether this attempt is a failover from a previously failed client.
109+
:type is_failover_request: bool
110+
"""
100111
settings_refreshed = False
101112
headers = self._update_correlation_context_header(
102113
kwargs.pop("headers", {}),

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_azureappconfigurationproviderbase.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def _generate_allocation_id(feature_flag_value: Dict[str, JSON]) -> Optional[str
179179
seed=123abc\ndefault_when_enabled=Control\npercentiles=0,Control,20;20,Test,100\nvariants=Control,standard;Test,special # pylint:disable=line-too-long
180180
181181
:param Dict[str, JSON] feature_flag_value: The feature to generate an allocation ID for.
182-
:rtype: str
182+
:rtype: Optional[str]
183183
:return: The allocation ID.
184184
"""
185185

@@ -303,11 +303,11 @@ def values(self) -> ValuesView[Union[str, Mapping[str, Any]]]:
303303
resolved.
304304
305305
:return: A list of values loaded from Azure App Configuration. The values are either Strings or JSON objects,
306-
based on there content type.
306+
based on their content type.
307307
:rtype: ValuesView[Union[str, Mapping[str, Any]]]
308308
"""
309309
with self._update_lock:
310-
return (self._dict).values()
310+
return self._dict.values()
311311

312312
@overload
313313
def get(self, key: str, default: None = None) -> Union[str, JSON, None]: ...
@@ -440,7 +440,9 @@ def _update_correlation_context_header(
440440

441441
def _deduplicate_settings(self, configuration_settings: List[ConfigurationSetting]) -> List[ConfigurationSetting]:
442442
"""
443-
Deduplicates configuration settings by key.
443+
Deduplicates configuration settings by key, ignoring label. The provider exposes a flat
444+
key->value mapping, so when multiple selectors return the same key the last one wins,
445+
regardless of label.
444446
445447
:param List[ConfigurationSetting] configuration_settings: The list of configuration settings to deduplicate
446448
:return: A list of unique configuration settings

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_client_manager.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import time
88
import random
99
from dataclasses import dataclass
10-
from typing import Tuple, Union, Dict, List, Optional, Mapping
10+
from typing import Tuple, Union, List, Optional, Mapping
1111
from typing_extensions import Self
1212
from azure.core import MatchConditions
1313
from azure.core.tracing.decorator import distributed_trace
@@ -101,14 +101,14 @@ def from_connection_string(
101101
)
102102

103103
def _check_configuration_setting(
104-
self, key: str, label: str, etag: Optional[str], headers: Dict[str, str], **kwargs
104+
self, key: str, label: str, etag: Optional[str], headers: Mapping[str, str], **kwargs
105105
) -> Tuple[bool, Union[ConfigurationSetting, None]]:
106106
"""
107107
Checks if the configuration setting have been updated since the last refresh.
108108
109-
:param str key: key to check for chances
109+
:param str key: key to check for changes
110110
:param str label: label to check for changes
111-
:param str etag: etag to check for changes
111+
:param Optional[str] etag: etag to check for changes
112112
:param Mapping[str, str] headers: headers to use for the request
113113
:return: A tuple with the first item being true/false if a change is detected. The second item is the updated
114114
value if a change was detected.
@@ -284,16 +284,16 @@ def check_feature_flag_page_etags(
284284

285285
@distributed_trace
286286
def get_updated_watched_settings(
287-
self, watched_settings: Mapping[Tuple[str, str], Optional[str]], headers: Dict[str, str], **kwargs
287+
self, watched_settings: Mapping[Tuple[str, str], Optional[str]], headers: Mapping[str, str], **kwargs
288288
) -> Mapping[Tuple[str, str], Optional[str]]:
289289
"""
290290
Checks if any of the watch keys have changed, and updates them if they have.
291291
292292
:param Mapping[Tuple[str, str], Optional[str]] watched_settings: The configuration settings to check for changes
293293
:param Mapping[str, str] headers: The headers to use for the request
294294
295-
:return: Updated value of the configuration watched settings.
296-
:rtype: Union[Dict[Tuple[str, str], str], None]
295+
:return: Updated value of the configuration watched settings. Empty if no change was detected.
296+
:rtype: Mapping[Tuple[str, str], Optional[str]]
297297
"""
298298
updated_watched_settings = dict(watched_settings)
299299
trigger_refresh = False
@@ -319,8 +319,8 @@ def get_configuration_setting(self, key: str, label: str, **kwargs) -> Optional[
319319
320320
:param str key: The key of the configuration setting
321321
:param str label: The label of the configuration setting
322-
:return: The configuration setting
323-
:rtype: ConfigurationSetting
322+
:return: The configuration setting, or None when the supplied ETag has not been modified.
323+
:rtype: Optional[ConfigurationSetting]
324324
"""
325325
return self._client.get_configuration_setting(key=key, label=label, **kwargs)
326326

@@ -403,12 +403,12 @@ def __init__(
403403
endpoint: str,
404404
credential: Optional["TokenCredential"],
405405
user_agent: str,
406-
retry_total,
407-
retry_backoff_max,
408-
replica_discovery_enabled,
409-
min_backoff_sec,
410-
max_backoff_sec,
411-
load_balancing_enabled,
406+
retry_total: int,
407+
retry_backoff_max: int,
408+
replica_discovery_enabled: bool,
409+
min_backoff_sec: int,
410+
max_backoff_sec: int,
411+
load_balancing_enabled: bool,
412412
**kwargs,
413413
):
414414
super(ConfigurationClientManager, self).__init__(
@@ -445,6 +445,7 @@ def get_next_active_client(self) -> Optional[_ConfigurationClientWrapper]:
445445
method returns None.
446446
447447
:return: The next client to be used for the request.
448+
:rtype: Optional[_ConfigurationClientWrapper]
448449
"""
449450
if not self._active_clients:
450451
self._last_active_client_name = ""

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_client_manager_base.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@
55
# -------------------------------------------------------------------------
66
import random
77
from dataclasses import dataclass
8-
from typing import Optional, Mapping, Any
8+
from typing import Optional
99

1010
FALLBACK_CLIENT_REFRESH_EXPIRED_INTERVAL = 3600 # 1 hour in seconds
1111
MINIMAL_CLIENT_REFRESH_INTERVAL = 30 # 30 seconds
1212

13-
JSON = Mapping[str, Any]
14-
1513

1614
@dataclass
1715
class _ConfigurationClientWrapperBase:
@@ -23,12 +21,12 @@ def __init__(
2321
self,
2422
endpoint: str,
2523
user_agent: str,
26-
retry_total,
27-
retry_backoff_max,
28-
replica_discovery_enabled,
29-
min_backoff_sec,
30-
max_backoff_sec,
31-
_load_balancing_enabled: bool,
24+
retry_total: int,
25+
retry_backoff_max: int,
26+
replica_discovery_enabled: bool,
27+
min_backoff_sec: int,
28+
max_backoff_sec: int,
29+
load_balancing_enabled: bool,
3230
**kwargs,
3331
):
3432
self._last_active_client_name = ""
@@ -41,9 +39,10 @@ def __init__(
4139
self._args = dict(kwargs)
4240
self._min_backoff_sec = min_backoff_sec
4341
self._max_backoff_sec = max_backoff_sec
44-
self._load_balancing_enabled = _load_balancing_enabled
42+
self._load_balancing_enabled = load_balancing_enabled
4543

4644
def _calculate_backoff(self, attempts: int) -> float:
45+
4746
max_attempts = 63
4847
ms_per_second = 1000 # 1 Second in milliseconds
4948

@@ -55,8 +54,7 @@ def _calculate_backoff(self, attempts: int) -> float:
5554

5655
calculated_milliseconds = max(1, min_backoff_milliseconds) * (1 << min(attempts, max_attempts))
5756

58-
if calculated_milliseconds > max_backoff_milliseconds or calculated_milliseconds <= 0:
59-
calculated_milliseconds = max_backoff_milliseconds
57+
calculated_milliseconds = min(calculated_milliseconds, max_backoff_milliseconds)
6058

6159
return min_backoff_milliseconds + (
6260
random.uniform(0.0, 1.0) * (calculated_milliseconds - min_backoff_milliseconds) # nosec

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_constants.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
# Environment Variable Constants
2020
# ------------------------------------------------------------------------
2121
REQUEST_TRACING_DISABLED_ENVIRONMENT_VARIABLE = "AZURE_APP_CONFIGURATION_TRACING_DISABLED"
22-
AzureFunctionEnvironmentVariable = "FUNCTIONS_EXTENSION_VERSION"
23-
AzureWebAppEnvironmentVariable = "WEBSITE_SITE_NAME"
24-
ContainerAppEnvironmentVariable = "CONTAINER_APP_NAME"
25-
KubernetesEnvironmentVariable = "KUBERNETES_PORT"
26-
ServiceFabricEnvironmentVariable = "Fabric_NodeName" # cspell:disable-line
22+
AZURE_FUNCTION_ENVIRONMENT_VARIABLE = "FUNCTIONS_EXTENSION_VERSION"
23+
AZURE_WEB_APP_ENVIRONMENT_VARIABLE = "WEBSITE_SITE_NAME"
24+
CONTAINER_APP_ENVIRONMENT_VARIABLE = "CONTAINER_APP_NAME"
25+
KUBERNETES_ENVIRONMENT_VARIABLE = "KUBERNETES_PORT"
26+
SERVICE_FABRIC_ENVIRONMENT_VARIABLE = "Fabric_NodeName" # cspell:disable-line
2727

2828
# ------------------------------------------------------------------------
2929
# Telemetry and Tracing Constants
@@ -38,9 +38,9 @@
3838
APP_CONFIG_AI_MIME_PROFILE = "https://azconfig.io/mime-profiles/ai/"
3939
APP_CONFIG_AICC_MIME_PROFILE = "https://azconfig.io/mime-profiles/ai/chat-completion"
4040

41-
# =============================================================================
41+
# ------------------------------------------------------------------------
4242
# Startup Retry Constants
43-
# =============================================================================
43+
# ------------------------------------------------------------------------
4444
# Timeout
4545
DEFAULT_STARTUP_TIMEOUT = 100 # seconds
4646

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_json.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def _find_string_end(text: str, index: int) -> int:
4343

4444
def remove_json_comments(text: str) -> str:
4545
"""
46-
Removes comments from a JSON file. Supports //, and /* ... */ comments.
46+
Removes comments from a JSON string. Supports //, and /* ... */ comments.
4747
Returns as string.
4848
4949
:param text: The input JSON string with comments.

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_key_vault/_secret_provider.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# -------------------------------------------------------------------------
6-
from typing import Mapping, Any, Dict
6+
from typing import Any, Dict
77
from azure.appconfiguration import SecretReferenceConfigurationSetting # type:ignore # pylint:disable=no-name-in-module
88
from azure.keyvault.secrets import SecretClient, KeyVaultSecretIdentifier
99
from azure.core.exceptions import ServiceRequestError
1010
from ._secret_provider_base import _SecretProviderBase
1111

12-
JSON = Mapping[str, Any]
13-
1412

1513
class SecretProvider(_SecretProviderBase):
1614

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_key_vault/_secret_provider_base.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
# license information.
55
# -------------------------------------------------------------------------
66
from typing import (
7-
Mapping,
87
Any,
9-
TypeVar,
108
Optional,
119
Dict,
1210
Tuple,
@@ -15,9 +13,6 @@
1513
from azure.keyvault.secrets import KeyVaultSecretIdentifier
1614
from .._azureappconfigurationproviderbase import _RefreshTimer
1715

18-
JSON = Mapping[str, Any]
19-
_T = TypeVar("_T")
20-
2116

2217
class _SecretProviderBase:
2318

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_load.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# -------------------------------------------------------------------------
66
import datetime
77
from typing import (
8+
Any,
89
Callable,
910
List,
1011
Mapping,
@@ -24,10 +25,11 @@
2425
)
2526
from ._azureappconfigurationprovider import (
2627
AzureAppConfigurationProvider,
27-
JSON,
2828
_buildprovider,
2929
)
3030

31+
JSON = Mapping[str, Any]
32+
3133

3234
@overload
3335
def load( # pylint: disable=docstring-keyword-should-match-keyword-only
@@ -132,7 +134,7 @@ def load( # pylint: disable=docstring-keyword-should-match-keyword-only
132134
:keyword str connection_string: Connection string for App Configuration resource.
133135
:keyword Optional[List[~azure.appconfiguration.provider.SettingSelector]] selects: List of setting selectors to
134136
filter configuration settings
135-
:keyword trim_prefixes: Optional[List[str]] trim_prefixes: List of prefixes to trim from configuration keys
137+
:keyword Optional[List[str]] trim_prefixes: List of prefixes to trim from configuration keys
136138
:keyword ~azure.core.credentials.TokenCredential keyvault_credential: A credential for authenticating with the key
137139
vault. This is optional if keyvault_client_configs is provided.
138140
:keyword Mapping[str, Mapping] keyvault_client_configs: A Mapping of SecretClient endpoints to client
@@ -145,9 +147,6 @@ def load( # pylint: disable=docstring-keyword-should-match-keyword-only
145147
:keyword List[Tuple[str, str]] refresh_on: One or more settings whose modification will trigger a full refresh
146148
after a fixed interval. This should be a list of Key-Label pairs for specific settings (filters and wildcards are
147149
not supported).
148-
:keyword refresh_on: One or more settings whose modification will trigger a full refresh after a fixed interval.
149-
This should be a list of Key-Label pairs for specific settings (filters and wildcards are not supported).
150-
:paramtype refresh_on: List[Tuple[str, str]]
151150
:keyword int refresh_interval: The minimum time in seconds between when a call to `refresh` will actually trigger a
152151
service call to update the settings. Default value is 30 seconds.
153152
:keyword refresh_enabled: Optional flag to enable or disable refreshing of configuration settings. Defaults to

0 commit comments

Comments
 (0)