15
15
Any ,
16
16
Callable ,
17
17
Dict ,
18
- Iterable ,
19
18
Mapping ,
20
19
Optional ,
21
20
overload ,
22
21
List ,
23
22
Tuple ,
24
23
TYPE_CHECKING ,
25
24
Union ,
25
+ Iterator ,
26
+ KeysView ,
27
+ ItemsView ,
28
+ ValuesView ,
29
+ TypeVar ,
26
30
)
27
- from azure .appconfiguration import ( # pylint:disable=no-name-in-module
31
+ from azure .appconfiguration import ( # type:ignore # pylint:disable=no-name-in-module
28
32
AzureAppConfigurationClient ,
29
33
FeatureFlagConfigurationSetting ,
30
34
SecretReferenceConfigurationSetting ,
49
53
if TYPE_CHECKING :
50
54
from azure .core .credentials import TokenCredential
51
55
52
- JSON = Union [ str , Mapping [str , Any ] ] # pylint: disable=unsubscriptable-object
53
-
56
+ JSON = Mapping [str , Any ] # pylint: disable=unsubscriptable-object
57
+ _T = TypeVar ( "_T" )
54
58
logger = logging .getLogger (__name__ )
55
59
56
60
min_uptime = 5
@@ -63,6 +67,9 @@ def load(
63
67
* ,
64
68
selects : Optional [List [SettingSelector ]] = None ,
65
69
trim_prefixes : Optional [List [str ]] = None ,
70
+ keyvault_credential : Optional ["TokenCredential" ] = None ,
71
+ keyvault_client_configs : Optional [Mapping [str , JSON ]] = None ,
72
+ secret_resolver : Optional [Callable [[str ], str ]] = None ,
66
73
key_vault_options : Optional [AzureAppConfigurationKeyVaultOptions ] = None ,
67
74
refresh_on : Optional [List [Tuple [str , str ]]] = None ,
68
75
refresh_interval : int = 30 ,
@@ -109,6 +116,9 @@ def load(
109
116
connection_string : str ,
110
117
selects : Optional [List [SettingSelector ]] = None ,
111
118
trim_prefixes : Optional [List [str ]] = None ,
119
+ keyvault_credential : Optional ["TokenCredential" ] = None ,
120
+ keyvault_client_configs : Optional [Mapping [str , JSON ]] = None ,
121
+ secret_resolver : Optional [Callable [[str ], str ]] = None ,
112
122
key_vault_options : Optional [AzureAppConfigurationKeyVaultOptions ] = None ,
113
123
refresh_on : Optional [List [Tuple [str , str ]]] = None ,
114
124
refresh_interval : int = 30 ,
@@ -201,8 +211,8 @@ def load(*args, **kwargs) -> "AzureAppConfigurationProvider":
201
211
for (key , label ), etag in provider ._refresh_on .items ():
202
212
if not etag :
203
213
try :
204
- sentinel = provider ._client .get_configuration_setting (key , label , headers = headers )
205
- provider ._refresh_on [(key , label )] = sentinel .etag
214
+ sentinel = provider ._client .get_configuration_setting (key , label , headers = headers ) # type:ignore
215
+ provider ._refresh_on [(key , label )] = sentinel .etag # type:ignore
206
216
except HttpResponseError as e :
207
217
if e .status_code == 404 :
208
218
# If the sentinel is not found a refresh should be triggered when it is created.
@@ -279,15 +289,17 @@ def _buildprovider(
279
289
** kwargs
280
290
)
281
291
return provider
282
- provider ._client = AzureAppConfigurationClient (
283
- endpoint ,
284
- credential ,
285
- user_agent = user_agent ,
286
- retry_total = retry_total ,
287
- retry_backoff_max = retry_backoff_max ,
288
- ** kwargs
289
- )
290
- return provider
292
+ if endpoint is not None and credential is not None :
293
+ provider ._client = AzureAppConfigurationClient (
294
+ endpoint ,
295
+ credential ,
296
+ user_agent = user_agent ,
297
+ retry_total = retry_total ,
298
+ retry_backoff_max = retry_backoff_max ,
299
+ ** kwargs
300
+ )
301
+ return provider
302
+ raise ValueError ("Please pass either endpoint and credential, or a connection string." )
291
303
292
304
293
305
def _resolve_keyvault_reference (
@@ -320,7 +332,9 @@ def _resolve_keyvault_reference(
320
332
provider ._secret_clients [vault_url ] = referenced_client
321
333
322
334
if referenced_client :
323
- return referenced_client .get_secret (keyvault_identifier .name , version = keyvault_identifier .version ).value
335
+ secret_value = referenced_client .get_secret (keyvault_identifier .name , version = keyvault_identifier .version ).value
336
+ if secret_value is not None :
337
+ return secret_value
324
338
325
339
if provider ._secret_resolver :
326
340
return provider ._secret_resolver (config .secret_id )
@@ -352,7 +366,7 @@ def _is_json_content_type(content_type: str) -> bool:
352
366
353
367
def _build_sentinel (setting : Union [str , Tuple [str , str ]]) -> Tuple [str , str ]:
354
368
try :
355
- key , label = setting
369
+ key , label = setting # type:ignore
356
370
except IndexError :
357
371
key = setting
358
372
label = EMPTY_LABEL
@@ -417,18 +431,17 @@ class AzureAppConfigurationProvider(Mapping[str, Union[str, JSON]]): # pylint:
417
431
418
432
def __init__ (self , ** kwargs ) -> None :
419
433
self ._dict : Dict [str , str ] = {}
420
- self ._trim_prefixes : List [str ] = []
421
434
self ._client : Optional [AzureAppConfigurationClient ] = None
422
435
self ._secret_clients : Dict [str , SecretClient ] = {}
423
436
self ._selects : List [SettingSelector ] = kwargs .pop (
424
437
"selects" , [SettingSelector (key_filter = "*" , label_filter = EMPTY_LABEL )]
425
438
)
426
439
427
440
trim_prefixes : List [str ] = kwargs .pop ("trim_prefixes" , [])
428
- self ._trim_prefixes = sorted (trim_prefixes , key = len , reverse = True )
441
+ self ._trim_prefixes : List [ str ] = sorted (trim_prefixes , key = len , reverse = True )
429
442
430
443
refresh_on : List [Tuple [str , str ]] = kwargs .pop ("refresh_on" , None ) or []
431
- self ._refresh_on : Mapping [Tuple [str , str ] : Optional [str ]] = {_build_sentinel (s ): None for s in refresh_on }
444
+ self ._refresh_on : Mapping [Tuple [str , str ], Optional [str ]] = {_build_sentinel (s ): None for s in refresh_on }
432
445
self ._refresh_timer : _RefreshTimer = _RefreshTimer (** kwargs )
433
446
self ._on_refresh_success : Optional [Callable ] = kwargs .pop ("on_refresh_success" , None )
434
447
self ._on_refresh_error : Optional [Callable [[Exception ], None ]] = kwargs .pop ("on_refresh_error" , None )
@@ -460,7 +473,7 @@ def refresh(self, **kwargs) -> None:
460
473
headers = _get_headers ("Watch" , uses_key_vault = self ._uses_key_vault , ** kwargs )
461
474
for (key , label ), etag in updated_sentinel_keys .items ():
462
475
try :
463
- updated_sentinel = self ._client .get_configuration_setting (
476
+ updated_sentinel = self ._client .get_configuration_setting ( # type:ignore
464
477
key = key ,
465
478
label = label ,
466
479
etag = etag ,
@@ -554,14 +567,14 @@ def _process_key_value(self, config):
554
567
return config .value
555
568
return config .value
556
569
557
- def __getitem__ (self , key : str ) -> str :
570
+ def __getitem__ (self , key : str ) -> Any :
558
571
# pylint:disable=docstring-missing-param,docstring-missing-return,docstring-missing-rtype
559
572
"""
560
573
Returns the value of the specified key.
561
574
"""
562
575
return self ._dict [key ]
563
576
564
- def __iter__ (self ) -> Iterable [str ]:
577
+ def __iter__ (self ) -> Iterator [str ]:
565
578
return self ._dict .__iter__ ()
566
579
567
580
def __len__ (self ) -> int :
@@ -574,40 +587,48 @@ def __contains__(self, __x: object) -> bool:
574
587
"""
575
588
return self ._dict .__contains__ (__x )
576
589
577
- def keys (self ) -> Iterable [str ]:
590
+ def keys (self ) -> KeysView [str ]:
578
591
"""
579
592
Returns a list of keys loaded from Azure App Configuration.
580
593
581
594
:return: A list of keys loaded from Azure App Configuration.
582
- :rtype: Iterable [str]
595
+ :rtype: KeysView [str]
583
596
"""
584
597
with self ._update_lock :
585
- return list (self ._dict .keys () )
598
+ return copy . deepcopy (self ._dict ) .keys ()
586
599
587
- def items (self ) -> Iterable [ Tuple [ str , Union [str , JSON ]]]:
600
+ def items (self ) -> ItemsView [ str , Union [str , Mapping [ str , Any ]]]:
588
601
"""
589
602
Returns a set-like object of key-value pairs loaded from Azure App Configuration. Any values that are Key Vault
590
603
references will be resolved.
591
604
592
605
:return: A set-like object of key-value pairs loaded from Azure App Configuration.
593
- :rtype: Iterable[Tuple[ str, Union[str, JSON ]]]
606
+ :rtype: ItemsView[ str, Union[str, Mapping[str, Any ]]]
594
607
"""
595
608
with self ._update_lock :
596
- return copy .deepcopy (self ._dict .items () )
609
+ return copy .deepcopy (self ._dict ) .items ()
597
610
598
- def values (self ) -> Iterable [Union [str , JSON ]]:
611
+ def values (self ) -> ValuesView [Union [str , Mapping [ str , Any ] ]]:
599
612
"""
600
613
Returns a list of values loaded from Azure App Configuration. Any values that are Key Vault references will be
601
614
resolved.
602
615
603
616
:return: A list of values loaded from Azure App Configuration. The values are either Strings or JSON objects,
604
- based on there content type.
605
- :rtype: Iterable[ [str], [JSON ]]
617
+ based on there content type.
618
+ :rtype: ValuesView[Union [str, Mapping[str, Any] ]]
606
619
"""
607
620
with self ._update_lock :
608
- return copy .deepcopy (list ((self ._dict .values ())))
621
+ return copy .deepcopy ((self ._dict )).values ()
622
+
623
+ @overload
624
+ def get (self , key : str , default : None = None ) -> Union [str , JSON , None ]:
625
+ ...
626
+
627
+ @overload
628
+ def get (self , key : str , default : Union [str , JSON , _T ]) -> Union [str , JSON , _T ]: # pylint: disable=signature-differs
629
+ ...
609
630
610
- def get (self , key : str , default : Optional [str ] = None ) -> Union [str , JSON ]:
631
+ def get (self , key : str , default : Optional [Union [ str , JSON , _T ]] = None ) -> Union [str , JSON , _T , None ]:
611
632
"""
612
633
Returns the value of the specified key. If the key does not exist, returns the default value.
613
634
@@ -618,7 +639,7 @@ def get(self, key: str, default: Optional[str] = None) -> Union[str, JSON]:
618
639
:rtype: Union[str, JSON]
619
640
"""
620
641
with self ._update_lock :
621
- return copy .deepcopy (self ._dict .get (key , default ) )
642
+ return copy .deepcopy (self ._dict ) .get (key , default )
622
643
623
644
def __eq__ (self , other : Any ) -> bool :
624
645
if not isinstance (other , AzureAppConfigurationProvider ):
@@ -640,15 +661,15 @@ def close(self) -> None:
640
661
"""
641
662
for client in self ._secret_clients .values ():
642
663
client .close ()
643
- self ._client .close ()
664
+ self ._client .close () # type: ignore
644
665
645
666
def __enter__ (self ) -> "AzureAppConfigurationProvider" :
646
- self ._client .__enter__ ()
667
+ self ._client .__enter__ () # type: ignore
647
668
for client in self ._secret_clients .values ():
648
669
client .__enter__ ()
649
670
return self
650
671
651
672
def __exit__ (self , * args ) -> None :
652
- self ._client .__exit__ (* args )
673
+ self ._client .__exit__ (* args ) # type: ignore
653
674
for client in self ._secret_clients .values ():
654
675
client .__exit__ ()
0 commit comments