Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use of functools.wraps unintentionally updates _GapicCallable class attributes #579

Open
XuanWang-Amos opened this issue Jan 3, 2024 · 1 comment
Labels
priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@XuanWang-Amos
Copy link
Contributor

We got a CBF after we imported a PR in grpc Python to g3 (stack trace listed below).

After some digging, we found that it's because the use of functools.wraps unintentionally updates _GapicCallable class attributes _target.

In the PR, we introduced a new attribute _target to grpc multi callables, but the implementation of functools.wraps did more than what's intended, it changed _target attribute of _GapicCallable to the ones from multi callables, causing _GapicCallable._target to change from a function to a byte string, I also provided an example below.

Code example

import functools

class _GapicCallable(object):

    def __init__(self, target):
        print(f"create _GapicCallable with target: {target}")
        self._target = target


    def __call__(self, *args, **kwargs):
        print(f"_GapicCallable.__call__._target: {self._target}")


def wrap_method(func):
  # Fix 2: adding updated
  # return functools.wraps(func, updated=[])(
  return functools.wraps(func)(
      _GapicCallable(func)
  )


class TestCallble:
  # Fix 1: adding __slots__
  # __slots__ = ["_target"]
  def __init__(self):
    self._target = "test"


test_callable = TestCallble()
wrapped = wrap_method(test_callable)  # At this point, _GapicCallable._target is a TestCallble object.
wrapped() # Here, _GapicCallable._target becomes "test" string.

Stack trace

Traceback (most recent call last):
  File "/build/work/3acefca9d25380652e9aea15ea6b9206b95e/google3/runfiles/google3/third_party/tink/python/tink/integration/gcpkms/_gcp_kms_client_integration_test.py", line 77, in test_not_bound_to_key_uri_encrypt_decrypt
    ciphertext = gcp_aead.encrypt(plaintext, associated_data)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/work/3acefca9d25380652e9aea15ea6b9206b95e/google3/runfiles/google3/third_party/tink/python/tink/integration/gcpkms/_gcp_kms_client.py", line 48, in encrypt
    response = self.client.encrypt(
               ^^^^^^^^^^^^^^^^^^^^
  File "/build/work/3acefca9d25380652e9aea15ea6b9206b95e/google3/runfiles/google3/third_party/py/google/cloud/kms_v1/gapic/key_management_service_client.py", line 1731, in encrypt
    return self._inner_api_calls["encrypt"](
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/work/3acefca9d25380652e9aea15ea6b9206b95e/google3/runfiles/google3/third_party/py/google/api_core/gapic_v1/method.py", line 141, in __call__
    return wrapped_func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/work/3acefca9d25380652e9aea15ea6b9206b95e/google3/runfiles/google3/third_party/py/google/api_core/retry.py", line 269, in retry_wrapped_func
    return retry_target(
           ^^^^^^^^^^^^^
  File "/build/work/3acefca9d25380652e9aea15ea6b9206b95e/google3/runfiles/google3/third_party/py/google/api_core/retry.py", line 176, in retry_target
    return target()
           ^^^^^^^^
  File "/build/work/3acefca9d25380652e9aea15ea6b9206b95e/google3/runfiles/google3/third_party/py/google/api_core/timeout.py", line 197, in func_with_timeout
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
TypeError: 'bytes' object is not callable

Suggested Fix

  • We'll add __slot__ to our multi-callable class. In the meantime, we suggest adding updated to functools.wraps to prevent similar issues from happening in the future.
@XuanWang-Amos
Copy link
Contributor Author

@parthea just FYI.

copybara-service bot pushed a commit to grpc/grpc that referenced this issue Jan 4, 2024
This reverts commit 96b9e8d.

[Implement OpenTelemetry PR](#35292) was [reverted](96b9e8d) because some tests started failing after import the changes to g3.

After investigation, we found root cause, it can be fixed both on our side and on gapic API side, we opened an issue to [gapic API team](googleapis/python-api-core#579), this PR will includes the fixes on our side.

<!--

If you know who should review your pull request, please assign it to that
person, otherwise the pull request would get assigned randomly.

If your pull request is for a specific language, please add the appropriate
lang label.

-->

Closes #35439

COPYBARA_INTEGRATE_REVIEW=#35439 from XuanWang-Amos:reapply_otel 0133564
PiperOrigin-RevId: 595746222
@parthea parthea added type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. priority: p2 Moderately-important priority. Fix may not be included in next release. labels Jan 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

No branches or pull requests

2 participants