Skip to content

Commit 7b0e562

Browse files
yuanqu72Copilot
andauthored
Appconfig provider ff endpoint (#48700)
* Load feature flags from the dedicated feature flag resource endpoint in the provider Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add tests.md doc link and fix recordings push command * Update recordings tag and apply black formatting fix * addressed comments * Fix null-value handling in entra_id_sample and clean up refresh sample sentinel/watch-key logic and timing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make refresh_sample_feature_flags.py self-contained by creating its own my_json key and looking up Beta by id instead of a fragile index Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enhance feature flag processing and clarify provider test instructions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * updated release date --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e2ac2c2 commit 7b0e562

35 files changed

Lines changed: 2762 additions & 175 deletions

‎sdk/appconfiguration/azure-appconfiguration-provider/CHANGELOG.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
# Release History
22

3-
## 2.5.1 (Unreleased)
3+
## 2.6.0b1 (2026-08-25)
44

55
### Features Added
66

7+
- Enhanced feature flags created via the dedicated feature flag resource endpoint (`FeatureFlagClient`/`FeatureFlag` in `azure-appconfiguration`) are now loaded automatically alongside key-value based feature flags whenever `feature_flag_enabled=True`. Both kinds are merged into the same `feature_management.feature_flags` list, with resource-based feature flags taking precedence over key-value based ones when they share the same name. No new `load()` options are required to opt in, and existing `feature_flag_selectors` filter both kinds.
8+
79
### Breaking Changes
810

11+
- Raised the minimum supported Python version to 3.10, matching the minimum required by `azure-appconfiguration>=1.10.0b1`. Dropped support for Python 3.7, 3.8, and 3.9.
12+
913
### Bugs Fixed
1014

1115
- Fixed a resource leak where replica clients that were no longer part of the auto-failover set were not closed during client refresh.
@@ -17,6 +21,7 @@
1721
### Other Changes
1822

1923
- Bumped minimum dependency on `azure-core` to `>=1.31.0`.
24+
- Bumped minimum dependency on `azure-appconfiguration` to `>=1.10.0b1` for `FeatureFlagClient`/`FeatureFlag` support.
2025

2126
## 2.5.0 (2026-05-22)
2227

‎sdk/appconfiguration/azure-appconfiguration-provider/README.md‎

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,11 @@ The provider can be configured to refresh configurations from the store on a set
158158
import os
159159
from azure.appconfiguration.provider import load, WatchKey
160160

161-
connection_string = os.environ["APPCONFIGURATION_CONNECTION_STRING"]
162-
163161
config = load(
164162
endpoint=endpoint,
165163
credential=credential,
166164
refresh_on=[WatchKey("Sentinel")],
167-
refresh_interval=60,
165+
refresh_interval=30,
168166
**kwargs,
169167
)
170168
```
@@ -361,14 +359,12 @@ To enable refresh for feature flags you need to enable refresh. This will allow
361359
import os
362360
from azure.appconfiguration.provider import load, WatchKey
363361

364-
connection_string = os.environ["APPCONFIGURATION_CONNECTION_STRING"]
365-
366362
config = load(
367363
endpoint=endpoint,
368364
credential=credential,
369365
refresh_on=[WatchKey("message")],
370366
refresh_on_feature_flags=True,
371-
refresh_interval=60,
367+
refresh_interval=30,
372368
feature_flag_enabled=True,
373369
feature_flag_refresh_enabled=True,
374370
**kwargs,
@@ -377,6 +373,47 @@ config = load(
377373

378374
<!-- END SNIPPET -->
379375

376+
### Loading Enhanced Feature Flags
377+
378+
Feature flags can also be created using the dedicated feature flag endpoint (via `FeatureFlagClient`/`FeatureFlag` in `azure-appconfiguration`), instead of as key-value configuration settings. These are referred to as enhanced feature flags. No additional `load()` options are required to load them — it happens automatically whenever `feature_flag_enabled=True`, and they are merged into the same `feature_management.feature_flags` list as key-value based feature flags, with enhanced feature flags taking precedence when both share the same name (`id` for feature flag).
379+
380+
<!-- SNIPPET:enhanced_feature_flag_sample.enhanced_feature_flag_loading -->
381+
382+
```python
383+
from azure.appconfiguration.provider import load
384+
385+
# Feature flags loaded from the enhanced feature flag endpoint are merged into the same
386+
# feature_management.feature_flags list as key-value based feature flags.
387+
config = load(endpoint=endpoint, credential=credential, feature_flag_enabled=True, **kwargs)
388+
feature_flags = config["feature_management"]["feature_flags"]
389+
enhanced_flag_beta = next(flag for flag in feature_flags if flag.get("id") == "EnhancedFeatureBeta")
390+
print(enhanced_flag_beta["enabled"])
391+
```
392+
393+
<!-- END SNIPPET -->
394+
395+
`FeatureFlagSelector` is the dedicated selector type for filtering enhanced feature flags by name, label, or tags, and is the recommended way to select enhanced feature flags.
396+
397+
<!-- SNIPPET:enhanced_feature_flag_sample.enhanced_feature_flag_selector_with_feature_flag_selector -->
398+
399+
```python
400+
from azure.appconfiguration.provider import load, FeatureFlagSelector
401+
402+
# FeatureFlagSelector is the dedicated selector type for filtering enhanced feature flags.
403+
config = load(
404+
endpoint=endpoint,
405+
credential=credential,
406+
feature_flag_enabled=True,
407+
feature_flag_selectors=[FeatureFlagSelector(name_filter="Enhanced*")],
408+
**kwargs,
409+
)
410+
feature_flags = config["feature_management"]["feature_flags"]
411+
enhanced_flag_beta = next(flag for flag in feature_flags if flag.get("id") == "EnhancedFeatureBeta")
412+
print(enhanced_flag_beta["enabled"])
413+
```
414+
415+
<!-- END SNIPPET -->
416+
380417
## JSON Content Type
381418

382419
Configuration settings with a JSON content type (e.g., `application/json`) are automatically deserialized into their corresponding Python objects when loaded by the provider.
@@ -405,8 +442,10 @@ from azure.appconfiguration.provider import load
405442

406443

407444
def my_mapper(setting):
408-
# Transform the setting as needed
409-
setting.value = setting.value.strip()
445+
# Transform the setting as needed. Some settings may have a None value, so guard against that
446+
# before calling string methods on it.
447+
if setting.value is not None:
448+
setting.value = setting.value.strip()
410449

411450

412451
config = load(endpoint=endpoint, credential=credential, configuration_mapper=my_mapper, **kwargs)
@@ -469,6 +508,12 @@ This library uses the standard [logging](https://docs.python.org/3/library/loggi
469508
* **Configuration not refreshing** — Make sure you are calling `config.refresh()` periodically (e.g., before each request in a web app). The provider does not auto-refresh in the background.
470509
* **Startup failures** — If the store is unreachable during startup, the provider will retry until `startup_timeout` (default 100 seconds) is exceeded. Increase this value if your store is expected to have high latency.
471510

511+
## Testing
512+
513+
(This content is for `azure-appconfiguration-provider` package developer only)
514+
515+
See [tests/tests.md](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/appconfiguration/azure-appconfiguration-provider/tests/tests.md) for instructions on running unit and integration tests, working with recordings, and setting up environment variables for local testing.
516+
472517
## Next steps
473518

474519
Check out our Django and Flask examples to see how to use the provider in a web application.

‎sdk/appconfiguration/azure-appconfiguration-provider/api.md‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace azure.appconfiguration.provider
88
*,
99
feature_flag_enabled: bool = False,
1010
feature_flag_refresh_enabled: bool = False,
11-
feature_flag_selectors: Optional[List[SettingSelector]] = ...,
11+
feature_flag_selectors: Optional[Union[List[SettingSelector], List[FeatureFlagSelector]]] = ...,
1212
key_vault_options: Optional[AzureAppConfigurationKeyVaultOptions] = ...,
1313
keyvault_client_configs: Optional[Mapping[str, JSON]] = ...,
1414
keyvault_credential: Optional[TokenCredential] = ...,
@@ -31,7 +31,7 @@ namespace azure.appconfiguration.provider
3131
connection_string: str,
3232
feature_flag_enabled: bool = False,
3333
feature_flag_refresh_enabled: bool = False,
34-
feature_flag_selectors: Optional[List[SettingSelector]] = ...,
34+
feature_flag_selectors: Optional[Union[List[SettingSelector], List[FeatureFlagSelector]]] = ...,
3535
key_vault_options: Optional[AzureAppConfigurationKeyVaultOptions] = ...,
3636
keyvault_client_configs: Optional[Mapping[str, JSON]] = ...,
3737
keyvault_credential: Optional[TokenCredential] = ...,
@@ -68,6 +68,17 @@ namespace azure.appconfiguration.provider
6868
def refresh(self, **kwargs) -> None: ...
6969

7070

71+
class azure.appconfiguration.provider.FeatureFlagSelector:
72+
73+
def __init__(
74+
self,
75+
*,
76+
label_filter: Optional[str] = NULL_CHAR,
77+
name_filter: Optional[str] = ...,
78+
tag_filters: Optional[List[str]] = ...
79+
): ...
80+
81+
7182
class azure.appconfiguration.provider.SettingSelector:
7283

7384
def __init__(
@@ -94,7 +105,7 @@ namespace azure.appconfiguration.provider.aio
94105
*,
95106
feature_flag_enabled: bool = False,
96107
feature_flag_refresh_enabled: bool = False,
97-
feature_flag_selectors: Optional[List[SettingSelector]] = ...,
108+
feature_flag_selectors: Optional[Union[List[SettingSelector], List[FeatureFlagSelector]]] = ...,
98109
key_vault_options: Optional[AzureAppConfigurationKeyVaultOptions] = ...,
99110
keyvault_client_configs: Optional[Mapping[str, JSON]] = ...,
100111
keyvault_credential: Optional[AsyncTokenCredential] = ...,
@@ -117,7 +128,7 @@ namespace azure.appconfiguration.provider.aio
117128
connection_string: str,
118129
feature_flag_enabled: bool = False,
119130
feature_flag_refresh_enabled: bool = False,
120-
feature_flag_selectors: Optional[List[SettingSelector]] = ...,
131+
feature_flag_selectors: Optional[Union[List[SettingSelector], List[FeatureFlagSelector]]] = ...,
121132
key_vault_options: Optional[AzureAppConfigurationKeyVaultOptions] = ...,
122133
keyvault_client_configs: Optional[Mapping[str, JSON]] = ...,
123134
keyvault_credential: Optional[AsyncTokenCredential] = ...,

‎sdk/appconfiguration/azure-appconfiguration-provider/assets.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/appconfiguration/azure-appconfiguration-provider",
5-
"Tag": "python/appconfiguration/azure-appconfiguration-provider_34a63910b7"
5+
"Tag": "python/appconfiguration/azure-appconfiguration-provider_d3ca89ab22"
66
}

‎sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from ._azureappconfigurationprovider import AzureAppConfigurationProvider
88
from ._models import (
99
AzureAppConfigurationKeyVaultOptions,
10+
FeatureFlagSelector,
1011
SettingSelector,
1112
WatchKey,
1213
)
@@ -18,6 +19,7 @@
1819
"load",
1920
"AzureAppConfigurationProvider",
2021
"AzureAppConfigurationKeyVaultOptions",
22+
"FeatureFlagSelector",
2123
"SettingSelector",
2224
"WatchKey",
2325
]

0 commit comments

Comments
 (0)