[gerrit_util] Cache resolved Authenticator as a class variable.
I noticed that the Authenticator is resolved maybe 5 or 6 times per
git-cl invocation. This should lead to more consistent behavior and
will likely be a bit faster, especially for SSOAuthenticator and
LuciAuthAuthenticator which involve subprocess invocations.
R=ayatane@chromium.org
Bug: 336351842
Change-Id: Id6c2873a6960a171305560acb98afe2c4f397295
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/5589865
Auto-Submit: Robbie Iannucci <iannucci@chromium.org>
Reviewed-by: Yiwei Zhang <yiwzhang@google.com>
Commit-Queue: Robbie Iannucci <iannucci@chromium.org>
diff --git a/gerrit_util.py b/gerrit_util.py
index aadcb6d..4e806df 100644
--- a/gerrit_util.py
+++ b/gerrit_util.py
@@ -174,6 +174,10 @@
class Authenticator(object):
"""Base authenticator class for authenticator implementations to subclass."""
+ # Cached Authenticator subclass instance, resolved via get().
+ _resolved: Optional[Authenticator] = None
+ _resolved_lock = threading.Lock()
+
def authenticate(self, conn: HttpConn):
"""Adds authentication information to the HttpConn."""
raise NotImplementedError()
@@ -193,47 +197,54 @@
environment."""
raise NotImplementedError()
- @staticmethod
- def get():
+ @classmethod
+ def get(cls):
"""Returns: (Authenticator) The identified Authenticator to use.
Probes the local system and its environment and identifies the
Authenticator instance to use.
+
+ The resolved Authenticator instance is cached as a class variable.
"""
- use_new_auth = newauth.Enabled()
+ with cls._resolved_lock:
+ if ret := cls._resolved:
+ return ret
- # Allow skipping SSOAuthenticator for local testing purposes.
- skip_sso = newauth.SkipSSO()
+ use_new_auth = newauth.Enabled()
- authenticators: List[Type[Authenticator]]
+ # Allow skipping SSOAuthenticator for local testing purposes.
+ skip_sso = newauth.SkipSSO()
- if use_new_auth:
- LOGGER.debug('Authenticator.get: using new auth stack.')
- authenticators = [
- SSOAuthenticator,
- LuciContextAuthenticator,
- GceAuthenticator,
- LuciAuthAuthenticator,
- ]
- if skip_sso:
- LOGGER.debug('Authenticator.get: skipping SSOAuthenticator.')
- authenticators = authenticators[1:]
- else:
- authenticators = [
- LuciContextAuthenticator,
- GceAuthenticator,
- CookiesAuthenticator,
- ]
+ if use_new_auth:
+ LOGGER.debug('Authenticator.get: using new auth stack.')
+ authenticators = [
+ SSOAuthenticator,
+ LuciContextAuthenticator,
+ GceAuthenticator,
+ LuciAuthAuthenticator,
+ ]
+ if skip_sso:
+ LOGGER.debug('Authenticator.get: skipping SSOAuthenticator.')
+ authenticators = authenticators[1:]
+ else:
+ authenticators = [
+ LuciContextAuthenticator,
+ GceAuthenticator,
+ CookiesAuthenticator,
+ ]
- for candidate in authenticators:
- if candidate.is_applicable():
- LOGGER.debug('Authenticator.get: Selected %s.',
- candidate.__name__)
- return candidate()
+ for candidate in authenticators:
+ if candidate.is_applicable():
+ LOGGER.debug('Authenticator.get: Selected %s.',
+ candidate.__name__)
+ ret = candidate()
+ cls._resolved = ret
+ return ret
- auth_names = ', '.join(a.__name__ for a in authenticators)
- raise ValueError(
- f"Could not find suitable authenticator, tried: [{auth_names}].")
+ auth_names = ', '.join(a.__name__ for a in authenticators)
+ raise ValueError(
+ f"Could not find suitable authenticator, tried: [{auth_names}]."
+ )
class SSOAuthenticator(Authenticator):
diff --git a/tests/git_cl_test.py b/tests/git_cl_test.py
index c7c4721..676c9f6 100755
--- a/tests/git_cl_test.py
+++ b/tests/git_cl_test.py
@@ -669,6 +669,7 @@
# It's important to reset settings to not have inter-tests interference.
git_cl.settings = git_cl.Settings()
self.addCleanup(mock.patch.stopall)
+ gerrit_util.Authenticator._resolved = None
def tearDown(self):
try:
@@ -3623,6 +3624,7 @@
self.mockGit = GitMocks()
mock.patch('scm.GIT.GetConfig', self.mockGit.GetConfig).start()
mock.patch('scm.GIT.SetConfig', self.mockGit.SetConfig).start()
+ gerrit_util.Authenticator._resolved = None
def testRunHook(self):
expected_results = {