From 9fc9f06a1e63f7b1ba98ee9cc994462dfca0c91f Mon Sep 17 00:00:00 2001
From: Anthonios Partheniou <partheniou@google.com>
Date: Tue, 4 Jun 2024 07:49:02 -0400
Subject: [PATCH] Revert "feat: add caching to GapicCallable (#527)"

This reverts commit d96eb5cdd8120bfec97d62b09512c6fecc325be8.
---
 google/api_core/gapic_v1/method.py | 73 +++++++++++++++++-------------
 tests/unit/gapic/test_method.py    | 21 ---------
 2 files changed, 41 insertions(+), 53 deletions(-)

diff --git a/google/api_core/gapic_v1/method.py b/google/api_core/gapic_v1/method.py
index 206549ea..0f14ea9c 100644
--- a/google/api_core/gapic_v1/method.py
+++ b/google/api_core/gapic_v1/method.py
@@ -42,6 +42,24 @@ class _MethodDefault(enum.Enum):
 so the default should be used."""
 
 
+def _is_not_none_or_false(value):
+    return value is not None and value is not False
+
+
+def _apply_decorators(func, decorators):
+    """Apply a list of decorators to a given function.
+
+    ``decorators`` may contain items that are ``None`` or ``False`` which will
+    be ignored.
+    """
+    filtered_decorators = filter(_is_not_none_or_false, reversed(decorators))
+
+    for decorator in filtered_decorators:
+        func = decorator(func)
+
+    return func
+
+
 class _GapicCallable(object):
     """Callable that applies retry, timeout, and metadata logic.
 
@@ -73,8 +91,6 @@ def __init__(
     ):
         self._target = target
         self._retry = retry
-        if isinstance(timeout, (int, float)):
-            timeout = TimeToDeadlineTimeout(timeout=timeout)
         self._timeout = timeout
         self._compression = compression
         self._metadata = metadata
@@ -84,42 +100,35 @@ def __call__(
     ):
         """Invoke the low-level RPC with retry, timeout, compression, and metadata."""
 
-        if compression is DEFAULT:
-            compression = self._compression
-        if compression is not None:
-            kwargs["compression"] = compression
+        if retry is DEFAULT:
+            retry = self._retry
 
-        # Add the user agent metadata to the call.
-        if self._metadata is not None:
-            try:
-                # attempt to concatenate default metadata with user-provided metadata
-                kwargs["metadata"] = (*kwargs["metadata"], *self._metadata)
-            except (KeyError, TypeError):
-                # if metadata is not provided, use just the default metadata
-                kwargs["metadata"] = self._metadata
-
-        call = self._build_wrapped_call(timeout, retry)
-        return call(*args, **kwargs)
-
-    @functools.lru_cache(maxsize=4)
-    def _build_wrapped_call(self, timeout, retry):
-        """
-        Build a wrapped callable that applies retry, timeout, and metadata logic.
-        """
-        wrapped_func = self._target
         if timeout is DEFAULT:
             timeout = self._timeout
-        elif isinstance(timeout, (int, float)):
+
+        if compression is DEFAULT:
+            compression = self._compression
+
+        if isinstance(timeout, (int, float)):
             timeout = TimeToDeadlineTimeout(timeout=timeout)
-        if timeout is not None:
-            wrapped_func = timeout(wrapped_func)
 
-        if retry is DEFAULT:
-            retry = self._retry
-        if retry is not None:
-            wrapped_func = retry(wrapped_func)
+        # Apply all applicable decorators.
+        wrapped_func = _apply_decorators(self._target, [retry, timeout])
+
+        # Add the user agent metadata to the call.
+        if self._metadata is not None:
+            metadata = kwargs.get("metadata", [])
+            # Due to the nature of invocation, None should be treated the same
+            # as not specified.
+            if metadata is None:
+                metadata = []
+            metadata = list(metadata)
+            metadata.extend(self._metadata)
+            kwargs["metadata"] = metadata
+        if self._compression is not None:
+            kwargs["compression"] = compression
 
-        return wrapped_func
+        return wrapped_func(*args, **kwargs)
 
 
 def wrap_method(
diff --git a/tests/unit/gapic/test_method.py b/tests/unit/gapic/test_method.py
index 370da501..d966f478 100644
--- a/tests/unit/gapic/test_method.py
+++ b/tests/unit/gapic/test_method.py
@@ -222,24 +222,3 @@ def test_wrap_method_with_call_not_supported():
     with pytest.raises(ValueError) as exc_info:
         google.api_core.gapic_v1.method.wrap_method(method, with_call=True)
     assert "with_call=True is only supported for unary calls" in str(exc_info.value)
-
-
-def test_benchmark_gapic_call():
-    """
-    Ensure the __call__ method performance does not regress from expectations
-
-    __call__ builds a new wrapped function using passed-in timeout and retry, but
-    subsequent calls are cached
-
-    Note: The threshold has been tuned for the CI workers. Test may flake on
-    slower hardware
-    """
-    from google.api_core.gapic_v1.method import _GapicCallable
-    from google.api_core.retry import Retry
-    from timeit import timeit
-
-    gapic_callable = _GapicCallable(
-        lambda *a, **k: 1, retry=Retry(), timeout=1010, compression=False
-    )
-    avg_time = timeit(lambda: gapic_callable(), number=10_000)
-    assert avg_time < 0.4