2424
2525
2626from google .auth import exceptions
27- from google .auth import transport
27+ from google .auth import transport
2828from google .auth .transport import _mtls_helper
2929from google .auth .transport import mtls
3030from google .oauth2 import service_account
@@ -408,12 +408,12 @@ class _MTLSCallInterceptor(
408408 grpc .StreamStreamClientInterceptor ,
409409):
410410 """A gRPC client interceptor that provides automatic retry logic for mTLS certificate rotation.
411-
412- This interceptor wraps all gRPC client calls (unary and streaming) with retryable
413- futures or iterators. Its primary role is to monitor responses for `UNAUTHENTICATED`
414- errors. When an authentication failure occurs, it uses `_should_retry()` to check
415- if a new mTLS certificate is available. If a new certificate is found, it signals
416- its associated `_MTLSRefreshingChannel` wrapper to refresh the underlying gRPC
411+
412+ This interceptor wraps all gRPC client calls (unary and streaming) with retryable
413+ futures or iterators. Its primary role is to monitor responses for `UNAUTHENTICATED`
414+ errors. When an authentication failure occurs, it uses `_should_retry()` to check
415+ if a new mTLS certificate is available. If a new certificate is found, it signals
416+ its associated `_MTLSRefreshingChannel` wrapper to refresh the underlying gRPC
417417 channel's credentials and automatically replays the failed RPC.
418418 """
419419
@@ -423,15 +423,17 @@ def __init__(self):
423423
424424 def _should_retry (self , code , retry_count , attempt_cert ):
425425 """Determines if the RPC should be retried due to a certificate rotation.
426-
427- Returns a tuple: (should_retry, call_cert_bytes, call_key_bytes, passphrase).
428- """
426+
427+ Returns a tuple: (should_retry, call_cert_bytes, call_key_bytes, passphrase).
428+ """
429429 if code != grpc .StatusCode .UNAUTHENTICATED or not self ._wrapper :
430430 return False , None , None , None
431431
432432 if retry_count >= self ._max_retries :
433433 _LOGGER .debug (
434- "Max retries reached (%d/%d) for channel recreation." , retry_count , self ._max_retries
434+ "Max retries reached (%d/%d) for channel recreation." ,
435+ retry_count ,
436+ self ._max_retries ,
435437 )
436438 return False , None , None , None
437439
@@ -492,14 +494,14 @@ def __init__(self, target, factory_args, initial_channel, initial_cert):
492494 self ._lock = threading .Lock ()
493495 self ._subscribers = set ()
494496
495- def refresh_logic (self , count , call_cert_bytes = None , call_key_bytes = None , passphrase = None ):
497+ def refresh_logic (
498+ self , count , call_cert_bytes = None , call_key_bytes = None , passphrase = None
499+ ):
496500 with self ._lock :
497501 if not call_cert_bytes or self ._cached_cert == call_cert_bytes :
498502 return
499503
500- _LOGGER .debug (
501- "Wrapper: Refreshing mTLS channel. Retry count: %d" , count
502- )
504+ _LOGGER .debug ("Wrapper: Refreshing mTLS channel. Retry count: %d" , count )
503505 old_channel = self ._channel
504506
505507 if passphrase is not None :
@@ -630,15 +632,19 @@ def __next__(self):
630632 ("method" , "timeout" , "metadata" , "credentials" , "wait_for_ready" ),
631633)
632634
635+
633636class _DeadlineExceededError (grpc .RpcError , grpc .Call ):
634637 def __init__ (self , details ):
635638 super ().__init__ ()
636639 self ._details = details
640+
637641 def code (self ):
638642 return grpc .StatusCode .DEADLINE_EXCEEDED
643+
639644 def details (self ):
640645 return self ._details
641646
647+
642648class _RetryableUnaryResponseFuture (grpc .Future , grpc .Call ):
643649 def __init__ (
644650 self ,
@@ -698,7 +704,9 @@ def _start_call(self):
698704 elapsed = time .monotonic () - self ._start_time
699705 remaining = self ._initial_timeout - elapsed
700706 if remaining <= 0 :
701- raise _DeadlineExceededError ("Deadline Exceeded during retry resolution." )
707+ raise _DeadlineExceededError (
708+ "Deadline Exceeded during retry resolution."
709+ )
702710 call_details = _ClientCallDetails (
703711 method = call_details .method ,
704712 timeout = remaining ,
@@ -742,7 +750,9 @@ def _on_inner_future_done(self, inner_future):
742750 if can_replay and should_retry :
743751 if getattr (self ._interceptor , "_wrapper" , None ):
744752 try :
745- self ._interceptor ._wrapper .refresh_logic (1 , call_cert , call_key , pwd )
753+ self ._interceptor ._wrapper .refresh_logic (
754+ 1 , call_cert , call_key , pwd
755+ )
746756 except Exception as e :
747757 with self ._lock :
748758 self ._terminal_exception = e
@@ -757,18 +767,23 @@ def _on_inner_future_done(self, inner_future):
757767 self ._terminal_exception = e
758768
759769 if self ._interceptor ._wrapper :
760- chk_should_retry , chk_cert , chk_key , chk_pwd = self ._interceptor ._should_retry (
761- status_code , 0 , self ._attempt_cert
762- )
770+ (
771+ chk_should_retry ,
772+ chk_cert ,
773+ chk_key ,
774+ chk_pwd ,
775+ ) = self ._interceptor ._should_retry (status_code , 0 , self ._attempt_cert )
763776 if chk_should_retry :
764777 try :
765- self ._interceptor ._wrapper .refresh_logic (1 , chk_cert , chk_key , chk_pwd )
778+ self ._interceptor ._wrapper .refresh_logic (
779+ 1 , chk_cert , chk_key , chk_pwd
780+ )
766781 except Exception :
767782 pass
768783 with self ._lock :
769784 self ._completion_event .set ()
770785 callbacks_to_fire = list (self ._done_callbacks )
771-
786+
772787 for fn in callbacks_to_fire :
773788 try :
774789 fn (self )
@@ -929,7 +944,9 @@ def _start_call(self):
929944 elapsed = time .monotonic () - self ._start_time
930945 remaining = self ._initial_timeout - elapsed
931946 if remaining <= 0 :
932- raise _DeadlineExceededError ("Deadline Exceeded during retry resolution." )
947+ raise _DeadlineExceededError (
948+ "Deadline Exceeded during retry resolution."
949+ )
933950 call_details = _ClientCallDetails (
934951 method = call_details .method ,
935952 timeout = remaining ,
@@ -959,9 +976,12 @@ def _on_inner_call_done(self, inner_call):
959976 if self ._call is not inner_call :
960977 return
961978 # Intercept and suppress premature callbacks for UNAUTHENTICATED.
962- # __next__ inherently handles this error and manages triggering callbacks
979+ # __next__ inherently handles this error and manages triggering callbacks
963980 # later if retriies are exhausted.
964- if callable (getattr (inner_call , "code" , None )) and inner_call .code () == grpc .StatusCode .UNAUTHENTICATED :
981+ if (
982+ callable (getattr (inner_call , "code" , None ))
983+ and inner_call .code () == grpc .StatusCode .UNAUTHENTICATED
984+ ):
965985 return
966986 self ._trigger_callbacks ()
967987
@@ -994,42 +1014,52 @@ def __next__(self):
9941014 )
9951015 )
9961016
997- should_retry , call_cert , call_key , pwd = self ._interceptor ._should_retry (
1017+ (
1018+ should_retry ,
1019+ call_cert ,
1020+ call_key ,
1021+ pwd ,
1022+ ) = self ._interceptor ._should_retry (
9981023 status_code ,
9991024 self ._retry_count ,
10001025 getattr (self , "_attempt_cert" , None ),
10011026 )
10021027
1003- if (
1004- not self ._yielded_any_response
1005- and can_replay
1006- and should_retry
1007- ):
1028+ if not self ._yielded_any_response and can_replay and should_retry :
10081029 try :
10091030 if getattr (self ._interceptor , "_wrapper" , None ):
1010- self ._interceptor ._wrapper .refresh_logic (1 , call_cert , call_key , pwd )
1011-
1031+ self ._interceptor ._wrapper .refresh_logic (
1032+ 1 , call_cert , call_key , pwd
1033+ )
1034+
10121035 with self ._lock :
10131036 self ._retry_count += 1
10141037 self ._start_call ()
1015-
1038+
10161039 except Exception as fallback_e :
10171040 self ._trigger_callbacks ()
10181041 raise fallback_e
1019-
1042+
10201043 continue
10211044 else :
10221045 # Non-retryable error, check if another rotation happened while we were finishing
10231046 if getattr (self ._interceptor , "_wrapper" , None ):
1024- chk_should_retry , chk_cert , chk_key , chk_pwd = self ._interceptor ._should_retry (
1047+ (
1048+ chk_should_retry ,
1049+ chk_cert ,
1050+ chk_key ,
1051+ chk_pwd ,
1052+ ) = self ._interceptor ._should_retry (
10251053 status_code , 0 , getattr (self , "_attempt_cert" , None )
10261054 )
10271055 if chk_should_retry :
10281056 try :
1029- self ._interceptor ._wrapper .refresh_logic (1 , chk_cert , chk_key , chk_pwd )
1057+ self ._interceptor ._wrapper .refresh_logic (
1058+ 1 , chk_cert , chk_key , chk_pwd
1059+ )
10301060 except Exception :
1031- pass # Terminal anyway
1032-
1061+ pass # Terminal anyway
1062+
10331063 self ._trigger_callbacks ()
10341064 raise e
10351065
0 commit comments