@@ -500,6 +500,42 @@ def read_response(self, disable_decoding=False):
500500 DefaultParser = PythonParser
501501
502502
503+ class CredentialsProvider :
504+ def __init__ (self , username = "" , password = "" , supplier = None , * args , ** kwargs ):
505+ """
506+ Initialize a new Credentials Provider.
507+ :param supplier: a supplier function that returns the username and password.
508+ def supplier(arg1, arg2, ...) -> (username, password)
509+ For examples see examples/connection_examples.ipynb
510+ :param args: arguments to pass to the supplier function
511+ :param kwargs: keyword arguments to pass to the supplier function
512+ """
513+ self .username = username
514+ self .password = password
515+ self .supplier = supplier
516+ self .args = args
517+ self .kwargs = kwargs
518+
519+ def get_credentials (self ):
520+ if self .supplier :
521+ self .username , self .password = self .supplier (* self .args , ** self .kwargs )
522+ if self .username :
523+ auth_args = (self .username , self .password or "" )
524+ else :
525+ auth_args = (self .password ,)
526+ return auth_args
527+
528+ def get_password (self , call_supplier = True ):
529+ if call_supplier and self .supplier :
530+ self .username , self .password = self .supplier (* self .args , ** self .kwargs )
531+ return self .password
532+
533+ def get_username (self , call_supplier = True ):
534+ if call_supplier and self .supplier :
535+ self .username , self .password = self .supplier (* self .args , ** self .kwargs )
536+ return self .username
537+
538+
503539class Connection :
504540 "Manages TCP communication to and from a Redis server"
505541
@@ -526,6 +562,7 @@ def __init__(
526562 username = None ,
527563 retry = None ,
528564 redis_connect_func = None ,
565+ credentials_provider = None ,
529566 ):
530567 """
531568 Initialize a new Connection.
@@ -538,9 +575,10 @@ def __init__(
538575 self .host = host
539576 self .port = int (port )
540577 self .db = db
541- self .username = username
542578 self .client_name = client_name
543- self .password = password
579+ self .credentials_provider = credentials_provider
580+ if not self .credentials_provider and (username or password ):
581+ self .credentials_provider = CredentialsProvider (username , password )
544582 self .socket_timeout = socket_timeout
545583 self .socket_connect_timeout = socket_connect_timeout or socket_timeout
546584 self .socket_keepalive = socket_keepalive
@@ -699,12 +737,9 @@ def on_connect(self):
699737 "Initialize the connection, authenticate and select a database"
700738 self ._parser .on_connect (self )
701739
702- # if username and/or password are set, authenticate
703- if self .username or self .password :
704- if self .username :
705- auth_args = (self .username , self .password or "" )
706- else :
707- auth_args = (self .password ,)
740+ # if credentials provider is set, authenticate
741+ if self .credentials_provider :
742+ auth_args = self .credentials_provider .get_credentials ()
708743 # avoid checking health here -- PING will fail if we try
709744 # to check the health prior to the AUTH
710745 self .send_command ("AUTH" , * auth_args , check_health = False )
@@ -716,7 +751,11 @@ def on_connect(self):
716751 # server seems to be < 6.0.0 which expects a single password
717752 # arg. retry auth with just the password.
718753 # https://github.com/andymccurdy/redis-py/issues/1274
719- self .send_command ("AUTH" , self .password , check_health = False )
754+ self .send_command (
755+ "AUTH" ,
756+ self .credentials_provider .get_password (),
757+ check_health = False ,
758+ )
720759 auth_response = self .read_response ()
721760
722761 if str_if_bytes (auth_response ) != "OK" :
@@ -1074,6 +1113,7 @@ def __init__(
10741113 client_name = None ,
10751114 retry = None ,
10761115 redis_connect_func = None ,
1116+ credentials_provider = None ,
10771117 ):
10781118 """
10791119 Initialize a new UnixDomainSocketConnection.
@@ -1085,9 +1125,10 @@ def __init__(
10851125 self .pid = os .getpid ()
10861126 self .path = path
10871127 self .db = db
1088- self .username = username
10891128 self .client_name = client_name
1090- self .password = password
1129+ self .credentials_provider = credentials_provider
1130+ if not self .credentials_provider and (username or password ):
1131+ self .credentials_provider = CredentialsProvider (username , password )
10911132 self .socket_timeout = socket_timeout
10921133 self .retry_on_timeout = retry_on_timeout
10931134 if retry_on_error is SENTINEL :
0 commit comments