google-api-core 2.42.0__tar.gz → 2.43.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. {google_api_core-2.42.0/google_api_core.egg-info → google_api_core-2.43.0}/PKG-INFO +1 -1
  2. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/_observability.py +230 -1
  3. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/gapic_v1/method.py +38 -24
  4. google_api_core-2.43.0/google/api_core/gapic_v1/method_async.py +120 -0
  5. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/grpc_helpers.py +219 -24
  6. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/grpc_helpers_async.py +25 -19
  7. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/version.py +1 -1
  8. {google_api_core-2.42.0 → google_api_core-2.43.0/google_api_core.egg-info}/PKG-INFO +1 -1
  9. {google_api_core-2.42.0 → google_api_core-2.43.0}/google_api_core.egg-info/SOURCES.txt +1 -0
  10. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/gapic/test_method_async.py +60 -53
  11. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/test_grpc_helpers_async.py +273 -0
  12. google_api_core-2.43.0/tests/asyncio/test_observability_async.py +176 -0
  13. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/conftest.py +30 -0
  14. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/gapic/test_method.py +38 -0
  15. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_grpc_helpers.py +411 -0
  16. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_observability.py +141 -85
  17. google_api_core-2.42.0/google/api_core/gapic_v1/method_async.py +0 -245
  18. {google_api_core-2.42.0 → google_api_core-2.43.0}/LICENSE +0 -0
  19. {google_api_core-2.42.0 → google_api_core-2.43.0}/MANIFEST.in +0 -0
  20. {google_api_core-2.42.0 → google_api_core-2.43.0}/README.rst +0 -0
  21. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/__init__.py +0 -0
  22. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/_feature_gating_helpers.py +0 -0
  23. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/_python_package_support.py +0 -0
  24. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/_python_version_support.py +0 -0
  25. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/_rest_streaming_base.py +0 -0
  26. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/bidi.py +0 -0
  27. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/bidi_async.py +0 -0
  28. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/bidi_base.py +0 -0
  29. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/client_info.py +0 -0
  30. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/client_logging.py +0 -0
  31. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/client_options.py +0 -0
  32. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/datetime_helpers.py +0 -0
  33. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/exceptions.py +0 -0
  34. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/extended_operation.py +0 -0
  35. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/future/__init__.py +0 -0
  36. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/future/_helpers.py +0 -0
  37. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/future/async_future.py +0 -0
  38. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/future/base.py +0 -0
  39. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/future/polling.py +0 -0
  40. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/gapic_v1/__init__.py +0 -0
  41. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/gapic_v1/client_info.py +0 -0
  42. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/gapic_v1/config.py +0 -0
  43. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/gapic_v1/config_async.py +0 -0
  44. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/gapic_v1/requests.py +0 -0
  45. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/gapic_v1/routing_header.py +0 -0
  46. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/general_helpers.py +0 -0
  47. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/iam.py +0 -0
  48. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operation.py +0 -0
  49. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operation_async.py +0 -0
  50. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/__init__.py +0 -0
  51. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/abstract_operations_base_client.py +0 -0
  52. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/abstract_operations_client.py +0 -0
  53. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/operations_async_client.py +0 -0
  54. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/operations_client.py +0 -0
  55. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/operations_client_config.py +0 -0
  56. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/operations_rest_client_async.py +0 -0
  57. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/pagers.py +0 -0
  58. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/pagers_async.py +0 -0
  59. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/pagers_base.py +0 -0
  60. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/transports/__init__.py +0 -0
  61. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/transports/base.py +0 -0
  62. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/transports/rest.py +0 -0
  63. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/operations_v1/transports/rest_asyncio.py +0 -0
  64. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/page_iterator.py +0 -0
  65. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/page_iterator_async.py +0 -0
  66. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/path_template.py +0 -0
  67. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/protobuf_helpers.py +0 -0
  68. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/py.typed +0 -0
  69. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/rest_helpers.py +0 -0
  70. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/rest_streaming.py +0 -0
  71. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/rest_streaming_async.py +0 -0
  72. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/resumable_transfer/__init__.py +0 -0
  73. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/resumable_transfer/common.py +0 -0
  74. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/resumable_transfer/upload.py +0 -0
  75. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/resumable_transfer/upload_async.py +0 -0
  76. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/resumable_transfer/upload_state.py +0 -0
  77. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/retry/__init__.py +0 -0
  78. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/retry/retry_base.py +0 -0
  79. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/retry/retry_streaming.py +0 -0
  80. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/retry/retry_streaming_async.py +0 -0
  81. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/retry/retry_unary.py +0 -0
  82. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/retry/retry_unary_async.py +0 -0
  83. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/retry_async.py +0 -0
  84. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/timeout.py +0 -0
  85. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/universe.py +0 -0
  86. {google_api_core-2.42.0 → google_api_core-2.43.0}/google/api_core/version_header.py +0 -0
  87. {google_api_core-2.42.0 → google_api_core-2.43.0}/google_api_core.egg-info/dependency_links.txt +0 -0
  88. {google_api_core-2.42.0 → google_api_core-2.43.0}/google_api_core.egg-info/requires.txt +0 -0
  89. {google_api_core-2.42.0 → google_api_core-2.43.0}/google_api_core.egg-info/top_level.txt +0 -0
  90. {google_api_core-2.42.0 → google_api_core-2.43.0}/pyproject.toml +0 -0
  91. {google_api_core-2.42.0 → google_api_core-2.43.0}/setup.cfg +0 -0
  92. {google_api_core-2.42.0 → google_api_core-2.43.0}/setup.py +0 -0
  93. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/__init__.py +0 -0
  94. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/__init__.py +0 -0
  95. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/future/__init__.py +0 -0
  96. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/future/test_async_future.py +0 -0
  97. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/gapic/test_config_async.py +0 -0
  98. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/operations_v1/__init__.py +0 -0
  99. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/operations_v1/test_operations_async_client.py +0 -0
  100. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/retry/__init__.py +0 -0
  101. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/retry/test_retry_streaming_async.py +0 -0
  102. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/retry/test_retry_unary_async.py +0 -0
  103. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/test_bidi_async.py +0 -0
  104. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/test_operation_async.py +0 -0
  105. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/test_page_iterator_async.py +0 -0
  106. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/test_rest_streaming_async.py +0 -0
  107. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/asyncio/test_resumable_transfer_async.py +0 -0
  108. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/helpers.py +0 -0
  109. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/__init__.py +0 -0
  110. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/future/__init__.py +0 -0
  111. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/future/test__helpers.py +0 -0
  112. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/future/test_polling.py +0 -0
  113. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/gapic/test_client_info.py +0 -0
  114. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/gapic/test_config.py +0 -0
  115. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/gapic/test_requests.py +0 -0
  116. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/gapic/test_routing_header.py +0 -0
  117. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/operations_v1/__init__.py +0 -0
  118. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/operations_v1/test_operations_client.py +0 -0
  119. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/operations_v1/test_operations_rest_client.py +0 -0
  120. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/retry/__init__.py +0 -0
  121. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/retry/test_retry_base.py +0 -0
  122. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/retry/test_retry_imports.py +0 -0
  123. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/retry/test_retry_streaming.py +0 -0
  124. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/retry/test_retry_unary.py +0 -0
  125. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_bidi.py +0 -0
  126. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_client_info.py +0 -0
  127. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_client_logging.py +0 -0
  128. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_client_options.py +0 -0
  129. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_datetime_helpers.py +0 -0
  130. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_exceptions.py +0 -0
  131. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_extended_operation.py +0 -0
  132. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_feature_gating_helpers.py +0 -0
  133. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_iam.py +0 -0
  134. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_operation.py +0 -0
  135. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_packaging.py +0 -0
  136. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_page_iterator.py +0 -0
  137. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_path_template.py +0 -0
  138. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_protobuf_helpers.py +0 -0
  139. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_python_package_support.py +0 -0
  140. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_python_version_support.py +0 -0
  141. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_rest_helpers.py +0 -0
  142. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_rest_streaming.py +0 -0
  143. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_resumable_transfer.py +0 -0
  144. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_resumable_transfer_scenarios.py +0 -0
  145. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_timeout.py +0 -0
  146. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_universe.py +0 -0
  147. {google_api_core-2.42.0 → google_api_core-2.43.0}/tests/unit/test_version_header.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-api-core
3
- Version: 2.42.0
3
+ Version: 2.43.0
4
4
  Summary: Google API client core library
5
5
  Author-email: Google LLC <googleapis-packages@google.com>
6
6
  License: Apache 2.0
@@ -18,12 +18,20 @@
18
18
 
19
19
  from __future__ import annotations
20
20
 
21
+ import contextlib
21
22
  import urllib.parse
22
23
  from typing import TYPE_CHECKING, Any, Callable, Sequence
23
24
 
24
25
  from google.api_core import _feature_gating_helpers
25
26
  from google.api_core.client_options import ClientOptions
26
27
 
28
+ try:
29
+ from opentelemetry.instrumentation.utils import (
30
+ suppress_instrumentation as _suppress_instrumentation,
31
+ )
32
+ except ImportError:
33
+ _suppress_instrumentation = contextlib.nullcontext
34
+
27
35
  if TYPE_CHECKING:
28
36
  # flake8: grpc, trace, and ClientInterceptor are imported only for static analysis and type annotations
29
37
  # The 'noqa: F401' comment avoids flake8 "imported but not used" errors.
@@ -223,6 +231,140 @@ def _get_tracer_provider(
223
231
  return None
224
232
 
225
233
 
234
+ try:
235
+ # flake8: `grpc` imported conditionally for runtime interceptor base classes
236
+ import grpc # noqa: F811
237
+ from grpc import (
238
+ StreamStreamClientInterceptor as _SyncStreamStreamClientInterceptor,
239
+ )
240
+ from grpc import (
241
+ StreamUnaryClientInterceptor as _SyncStreamUnaryClientInterceptor,
242
+ )
243
+ from grpc import (
244
+ UnaryStreamClientInterceptor as _SyncUnaryStreamClientInterceptor,
245
+ )
246
+ from grpc import (
247
+ UnaryUnaryClientInterceptor as _SyncUnaryUnaryClientInterceptor,
248
+ )
249
+ from grpc.aio import (
250
+ StreamStreamClientInterceptor as _AsyncStreamStreamClientInterceptor,
251
+ )
252
+ from grpc.aio import (
253
+ StreamUnaryClientInterceptor as _AsyncStreamUnaryClientInterceptor,
254
+ )
255
+ from grpc.aio import (
256
+ UnaryStreamClientInterceptor as _AsyncUnaryStreamClientInterceptor,
257
+ )
258
+ from grpc.aio import (
259
+ UnaryUnaryClientInterceptor as _AsyncUnaryUnaryClientInterceptor,
260
+ )
261
+ except ImportError: # pragma: NO COVER
262
+ # mypy: Fallbacks: grpc sentinel when optional grpc is not installed in the environment.
263
+ # and distinct empty classes avoid duplicate base class 'object' TypeError at runtime.
264
+ grpc = None # type: ignore[assignment]
265
+
266
+ class _SyncUnaryUnaryClientInterceptor: # type: ignore[no-redef]
267
+ __slots__ = ()
268
+
269
+ class _SyncUnaryStreamClientInterceptor: # type: ignore[no-redef]
270
+ __slots__ = ()
271
+
272
+ class _SyncStreamUnaryClientInterceptor: # type: ignore[no-redef]
273
+ __slots__ = ()
274
+
275
+ class _SyncStreamStreamClientInterceptor: # type: ignore[no-redef]
276
+ __slots__ = ()
277
+
278
+ class _AsyncUnaryUnaryClientInterceptor: # type: ignore[no-redef]
279
+ __slots__ = ()
280
+
281
+ class _AsyncUnaryStreamClientInterceptor: # type: ignore[no-redef]
282
+ __slots__ = ()
283
+
284
+ class _AsyncStreamUnaryClientInterceptor: # type: ignore[no-redef]
285
+ __slots__ = ()
286
+
287
+ class _AsyncStreamStreamClientInterceptor: # type: ignore[no-redef]
288
+ __slots__ = ()
289
+
290
+
291
+ class _SuppressingClientInterceptor(
292
+ _SyncUnaryUnaryClientInterceptor,
293
+ _SyncUnaryStreamClientInterceptor,
294
+ _SyncStreamUnaryClientInterceptor,
295
+ _SyncStreamStreamClientInterceptor,
296
+ ):
297
+ """Client interceptor that suppresses redundant downstream generic auto-instrumentation spans.
298
+
299
+ When users enable global OpenTelemetry gRPC auto-instrumentation (e.g. GrpcInstrumentorClient),
300
+ the underlying channel is wrapped with generic interceptors that emit bare-bones spans.
301
+ This interceptor wraps downstream calls in OpenTelemetry's native `suppress_instrumentation`
302
+ context manager so that our feature-rich Google Cloud SDK T4 span is emitted while
303
+ redundant generic downstream spans are bypassed.
304
+ """
305
+
306
+ __slots__ = ()
307
+
308
+ def intercept_unary_unary(
309
+ self, continuation: Any, client_call_details: Any, request: Any
310
+ ) -> Any:
311
+ with _suppress_instrumentation():
312
+ return continuation(client_call_details, request)
313
+
314
+ def intercept_unary_stream(
315
+ self, continuation: Any, client_call_details: Any, request: Any
316
+ ) -> Any:
317
+ with _suppress_instrumentation():
318
+ return continuation(client_call_details, request)
319
+
320
+ def intercept_stream_unary(
321
+ self, continuation: Any, client_call_details: Any, request_iterator: Any
322
+ ) -> Any:
323
+ with _suppress_instrumentation():
324
+ return continuation(client_call_details, request_iterator)
325
+
326
+ def intercept_stream_stream(
327
+ self, continuation: Any, client_call_details: Any, request_iterator: Any
328
+ ) -> Any:
329
+ with _suppress_instrumentation():
330
+ return continuation(client_call_details, request_iterator)
331
+
332
+
333
+ class _AsyncSuppressingClientInterceptor(
334
+ _AsyncUnaryUnaryClientInterceptor,
335
+ _AsyncUnaryStreamClientInterceptor,
336
+ _AsyncStreamUnaryClientInterceptor,
337
+ _AsyncStreamStreamClientInterceptor,
338
+ ):
339
+ """AsyncIO client interceptor that suppresses redundant downstream generic auto-instrumentation spans."""
340
+
341
+ __slots__ = ()
342
+
343
+ async def intercept_unary_unary(
344
+ self, continuation: Any, client_call_details: Any, request: Any
345
+ ) -> Any:
346
+ with _suppress_instrumentation():
347
+ return await continuation(client_call_details, request)
348
+
349
+ async def intercept_unary_stream(
350
+ self, continuation: Any, client_call_details: Any, request: Any
351
+ ) -> Any:
352
+ with _suppress_instrumentation():
353
+ return await continuation(client_call_details, request)
354
+
355
+ async def intercept_stream_unary(
356
+ self, continuation: Any, client_call_details: Any, request_iterator: Any
357
+ ) -> Any:
358
+ with _suppress_instrumentation():
359
+ return await continuation(client_call_details, request_iterator)
360
+
361
+ async def intercept_stream_stream(
362
+ self, continuation: Any, client_call_details: Any, request_iterator: Any
363
+ ) -> Any:
364
+ with _suppress_instrumentation():
365
+ return await continuation(client_call_details, request_iterator)
366
+
367
+
226
368
  def get_otel_interceptor(
227
369
  client_options: ClientOptions | dict[str, Any] | None = None,
228
370
  ) -> Callable[[grpc.Channel], grpc.Channel] | None:
@@ -249,8 +391,15 @@ def get_otel_interceptor(
249
391
  request_hook=request_hook,
250
392
  response_hook=_grpc_client_response_hook,
251
393
  )
394
+ suppressor = _SuppressingClientInterceptor()
252
395
 
253
396
  def otel_interceptor(channel: grpc.Channel) -> grpc.Channel:
397
+ if grpc is not None:
398
+ try:
399
+ channel = grpc.intercept_channel(channel, suppressor)
400
+ except (AttributeError, TypeError):
401
+ # Fallback if grpc lacks intercept_channel or custom channel type rejects it.
402
+ pass
254
403
  return otel_grpc.intercept_channel(channel, interceptor)
255
404
 
256
405
  otel_interceptor._is_otel_interceptor = True # type: ignore[attr-defined]
@@ -279,11 +428,91 @@ def get_otel_async_interceptor(
279
428
  endpoint_attrs = _extract_endpoint_attributes(client_options)
280
429
  request_hook = _make_grpc_client_request_hook(endpoint_attrs)
281
430
 
282
- return otel_grpc.aio_client_interceptors(
431
+ raw_interceptors = otel_grpc.aio_client_interceptors(
283
432
  tracer_provider=_get_tracer_provider(client_options),
284
433
  request_hook=request_hook,
285
434
  response_hook=_grpc_client_response_hook,
286
435
  )
436
+ interceptors = (
437
+ list(raw_interceptors)
438
+ if isinstance(raw_interceptors, (list, tuple))
439
+ else [raw_interceptors]
440
+ )
441
+ interceptors.append(_AsyncSuppressingClientInterceptor())
442
+
443
+ return interceptors
444
+
445
+
446
+ _CHANNEL_INTERCEPTOR_MAPPING = (
447
+ ("intercept_unary_unary", "_unary_unary_interceptors"),
448
+ ("intercept_unary_stream", "_unary_stream_interceptors"),
449
+ ("intercept_stream_unary", "_stream_unary_interceptors"),
450
+ ("intercept_stream_stream", "_stream_stream_interceptors"),
451
+ )
452
+
453
+
454
+ def apply_otel_async_channel_interceptors(
455
+ channel: Any,
456
+ client_options: ClientOptions | dict[str, Any] | Sequence[Any] | None = None,
457
+ ) -> Any:
458
+ """Creates and attaches OpenTelemetry client interceptors to the front of an async gRPC channel.
459
+
460
+ In grpc.aio, channels maintain internal interceptor lists evaluated in index order.
461
+ To ensure Google Cloud SDK telemetry wraps outside downstream interceptors and
462
+ suppresses ambient generic auto-instrumentation, our interceptors are prepended
463
+ to the front of the channel's interceptor lists.
464
+
465
+ Args:
466
+ channel: The async gRPC channel to intercept.
467
+ client_options (Optional[Union[ClientOptions, dict[str, Any], Sequence[Any]]]): The client
468
+ options used to resolve feature flags and tracer provider, or a sequence of
469
+ pre-resolved interceptors.
470
+
471
+ Returns:
472
+ The channel with interceptors prepended to its internal lists,
473
+ or the original channel if tracing is disabled or not installed.
474
+ """
475
+ otel_interceptors: Sequence[Any] | None
476
+ if isinstance(client_options, (ClientOptions, dict)) or client_options is None:
477
+ otel_interceptors = get_otel_async_interceptor(client_options)
478
+ elif isinstance(client_options, Sequence):
479
+ otel_interceptors = client_options
480
+ else:
481
+ otel_interceptors = None
482
+
483
+ if not otel_interceptors:
484
+ return channel
485
+
486
+ interceptor_list = list(otel_interceptors)
487
+
488
+ # Section 1: Typed interceptors - prepend matching interceptors to each
489
+ # corresponding channel list (unary-unary, unary-stream, etc.) in order.
490
+ # See note in the docstring about why prepending is important here.
491
+ for method_name, attr_name in _CHANNEL_INTERCEPTOR_MAPPING:
492
+ target_list = getattr(channel, attr_name, None)
493
+ if isinstance(target_list, list):
494
+ matching = [
495
+ i
496
+ for i in interceptor_list
497
+ if hasattr(i, method_name) and i not in target_list
498
+ ]
499
+ if matching:
500
+ target_list[:0] = matching
501
+
502
+ # Section 2: Untyped/generic fallback - prepend any remaining interceptors
503
+ # lacking standard method names directly to the unary-unary list.
504
+ unary_list = getattr(channel, "_unary_unary_interceptors", None)
505
+ if isinstance(unary_list, list):
506
+ standard_methods = tuple(method for method, _ in _CHANNEL_INTERCEPTOR_MAPPING)
507
+ fallback_items = [
508
+ i
509
+ for i in interceptor_list
510
+ if not any(hasattr(i, m) for m in standard_methods) and i not in unary_list
511
+ ]
512
+ if fallback_items:
513
+ unary_list[:0] = fallback_items
514
+
515
+ return channel
287
516
 
288
517
 
289
518
  _TRACE_CONTEXT_PROPAGATOR: Any = None
@@ -41,6 +41,10 @@ DEFAULT = _MethodDefault._DEFAULT_VALUE
41
41
  """Sentinel value indicating that a retry, timeout, or compression argument was unspecified,
42
42
  so the default should be used."""
43
43
 
44
+ _TRANSPORT_KIND_GRPC = "grpc"
45
+ _TRANSPORT_KIND_REST = "rest"
46
+ _DEFAULT_TRANSPORT_KIND = _TRANSPORT_KIND_GRPC
47
+
44
48
 
45
49
  def _is_not_none_or_false(value):
46
50
  return value is not None and value is not False
@@ -122,7 +126,7 @@ def _extract_rpc_identity(
122
126
  return method_str, service, method
123
127
 
124
128
 
125
- def _extract_status_code(exc: Optional[Exception]) -> str:
129
+ def _extract_status_code(exc: Optional[BaseException]) -> str:
126
130
  """Extract canonical status code name string from an exception.
127
131
 
128
132
  Status code name strings are resolved by inspecting the following locations:
@@ -132,7 +136,7 @@ def _extract_status_code(exc: Optional[Exception]) -> str:
132
136
  * Fallback: Defaults to the exception class name for standard Python errors.
133
137
 
134
138
  Args:
135
- exc (Optional[Exception]): The exception to extract the status code name from.
139
+ exc (Optional[BaseException]): The exception to extract the status code name from.
136
140
 
137
141
  Returns:
138
142
  str: The canonical status code name (e.g. "NOT_FOUND", "UNAVAILABLE") or class name.
@@ -166,7 +170,7 @@ def _extract_status_code(exc: Optional[Exception]) -> str:
166
170
  return target.__class__.__name__
167
171
 
168
172
 
169
- def _extract_error_attributes(exc: Optional[Exception]) -> dict[str, Any]:
173
+ def _extract_error_attributes(exc: Optional[BaseException]) -> dict[str, Any]:
170
174
  """Extract gcp.errors.* and error.type attributes from an exception.
171
175
 
172
176
  Error details and ErrorInfo structures are resolved by inspecting the following locations:
@@ -176,7 +180,7 @@ def _extract_error_attributes(exc: Optional[Exception]) -> dict[str, Any]:
176
180
  * Unified attribute extraction: Extracts domain, reason, and metadata from ErrorInfo or exception attributes.
177
181
 
178
182
  Args:
179
- exc (Optional[Exception]): An exception (such as GoogleAPICallError or grpc.RpcError) or ErrorInfo object.
183
+ exc (Optional[BaseException]): An exception (such as GoogleAPICallError or grpc.RpcError) or ErrorInfo object.
180
184
 
181
185
  Returns:
182
186
  dict[str, Any]: Extracted error attributes (e.g. gcp.errors.domain, error.type, gcp.errors.metadata.*).
@@ -228,9 +232,10 @@ def _extract_error_attributes(exc: Optional[Exception]) -> dict[str, Any]:
228
232
 
229
233
  # 5. Extract human-readable error description for cross-language PRD parity
230
234
  message = getattr(target_exc, "message", None)
231
- if not message and hasattr(target_exc, "details") and callable(target_exc.details):
232
- message = target_exc.details()
233
- if not message and isinstance(target_exc, Exception):
235
+ if not message and hasattr(target_exc, "details"):
236
+ details = target_exc.details
237
+ message = details() if callable(details) else details
238
+ if not message and isinstance(target_exc, BaseException):
234
239
  message = str(target_exc)
235
240
  if message:
236
241
  attrs["status.message"] = str(message)
@@ -271,6 +276,8 @@ class _GapicCallable(object):
271
276
  Allowed values for OpenTelemetry method tracing are "grpc" and "rest".
272
277
  """
273
278
 
279
+ _SUPPORTED_TRACING_KINDS = (_TRANSPORT_KIND_GRPC, _TRANSPORT_KIND_REST)
280
+
274
281
  def __init__(
275
282
  self,
276
283
  target,
@@ -282,7 +289,7 @@ class _GapicCallable(object):
282
289
  method_name=None,
283
290
  is_streaming=False,
284
291
  client_info=None,
285
- kind="grpc",
292
+ kind=_DEFAULT_TRANSPORT_KIND,
286
293
  ):
287
294
  self._target = target
288
295
  self._retry = retry
@@ -301,11 +308,11 @@ class _GapicCallable(object):
301
308
  self._default_metadata = self._static_metadata
302
309
 
303
310
  # Configure the OpenTelemetry span factory once at initialization.
304
- # For now, method tracing is gated to non-streaming gRPC calls where an explicit method_name is provided.
311
+ # For now, method tracing is gated to non-streaming calls where an explicit method_name is provided.
305
312
  self._start_span_fn = None
306
313
  if (
307
314
  not is_streaming
308
- and kind in ("grpc", "rest")
315
+ and kind in self._SUPPORTED_TRACING_KINDS
309
316
  and method_name is not None
310
317
  and _observability.is_otel_capabilities_enabled(client_options)
311
318
  ):
@@ -323,9 +330,8 @@ class _GapicCallable(object):
323
330
  tracer = trace.get_tracer("google.api_core")
324
331
 
325
332
  span_name, _, _ = _extract_rpc_identity(method_name)
326
- is_rest = kind in ("rest", "rest_asyncio")
327
333
  span_attributes = {
328
- "rpc.system.name": "http" if is_rest else "grpc",
334
+ "rpc.system.name": "http" if kind.startswith("rest") else "grpc",
329
335
  "rpc.method": span_name,
330
336
  }
331
337
  self._start_span_fn = functools.partial(
@@ -338,11 +344,7 @@ class _GapicCallable(object):
338
344
  # Gracefully disable tracing if OpenTelemetry or custom provider fails
339
345
  self._start_span_fn = None
340
346
 
341
- def __call__(
342
- self, *args, timeout=DEFAULT, retry=DEFAULT, compression=DEFAULT, **kwargs
343
- ):
344
- """Invoke the low-level RPC with retry, timeout, compression, and metadata."""
345
-
347
+ def _prepare_call(self, timeout, retry, compression, kwargs):
346
348
  if retry is DEFAULT:
347
349
  retry = self._retry
348
350
 
@@ -376,22 +378,26 @@ class _GapicCallable(object):
376
378
  if compression is not None:
377
379
  kwargs["compression"] = compression
378
380
 
381
+ return wrapped_func
382
+
383
+ @contextlib.contextmanager
384
+ def _trace_span(self):
379
385
  span_cm = contextlib.nullcontext()
380
386
  if self._start_span_fn is not None:
381
387
  try:
382
388
  span_cm = self._start_span_fn()
383
- except (
384
- Exception
385
- ): # Fail-open: proceed without span if tracing initialization fails
389
+ except Exception:
390
+ # Fail-open: proceed without span if tracing initialization fails
386
391
  span_cm = contextlib.nullcontext()
387
392
 
388
393
  with span_cm as span:
389
394
  try:
390
- result = wrapped_func(*args, **kwargs)
395
+ yield
391
396
  if span is not None and hasattr(span, "set_attribute"):
392
397
  span.set_attribute("rpc.response.status_code", "OK")
393
- return result
394
- except Exception as exc:
398
+ except (KeyboardInterrupt, SystemExit, GeneratorExit):
399
+ raise
400
+ except BaseException as exc:
395
401
  if span is not None and hasattr(span, "set_attribute"):
396
402
  span.set_attribute(
397
403
  "rpc.response.status_code", _extract_status_code(exc)
@@ -400,6 +406,14 @@ class _GapicCallable(object):
400
406
  span.set_attribute(k, v)
401
407
  raise
402
408
 
409
+ def __call__(
410
+ self, *args, timeout=DEFAULT, retry=DEFAULT, compression=DEFAULT, **kwargs
411
+ ):
412
+ """Invoke the low-level RPC with retry, timeout, compression, and metadata."""
413
+ wrapped_func = self._prepare_call(timeout, retry, compression, kwargs)
414
+ with self._trace_span():
415
+ return wrapped_func(*args, **kwargs)
416
+
403
417
 
404
418
  def wrap_method(
405
419
  func,
@@ -412,7 +426,7 @@ def wrap_method(
412
426
  client_options=None,
413
427
  method_name=None,
414
428
  is_streaming=False,
415
- kind="grpc",
429
+ kind=_DEFAULT_TRANSPORT_KIND,
416
430
  ):
417
431
  """Wrap an RPC method with common behavior.
418
432
 
@@ -0,0 +1,120 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """AsyncIO helpers for wrapping gRPC and REST methods with common functionality.
15
+
16
+ This is used by gapic clients to provide common error mapping, retry, timeout,
17
+ compression, pagination, and long-running operations to methods.
18
+ """
19
+
20
+ import functools
21
+ import inspect
22
+
23
+ from google.api_core import grpc_helpers_async
24
+ from google.api_core.gapic_v1 import client_info
25
+ from google.api_core.gapic_v1.method import ( # noqa: F401
26
+ DEFAULT,
27
+ USE_DEFAULT_METADATA,
28
+ _GapicCallable,
29
+ )
30
+
31
+ _TRANSPORT_KIND_GRPC_ASYNC = "grpc_asyncio"
32
+ _TRANSPORT_KIND_REST_ASYNC = "rest_asyncio"
33
+ _DEFAULT_ASYNC_TRANSPORT_KIND = _TRANSPORT_KIND_GRPC_ASYNC
34
+
35
+
36
+ class _AsyncGapicCallable(_GapicCallable):
37
+ """Async callable object that wraps an async RPC method with retry, timeout, metadata, and tracing."""
38
+
39
+ _SUPPORTED_TRACING_KINDS = (
40
+ _TRANSPORT_KIND_GRPC_ASYNC,
41
+ _TRANSPORT_KIND_REST_ASYNC,
42
+ )
43
+
44
+ async def __call__(
45
+ self, *args, timeout=DEFAULT, retry=DEFAULT, compression=DEFAULT, **kwargs
46
+ ):
47
+ """Invoke the low-level async RPC with retry, timeout, compression, and metadata."""
48
+ wrapped_func = self._prepare_call(timeout, retry, compression, kwargs)
49
+ with self._trace_span():
50
+ res = wrapped_func(*args, **kwargs)
51
+ if inspect.isawaitable(res):
52
+ return await res
53
+ return res
54
+
55
+
56
+ def wrap_method(
57
+ func,
58
+ default_retry=None,
59
+ default_timeout=None,
60
+ default_compression=None,
61
+ client_info=client_info.DEFAULT_CLIENT_INFO,
62
+ kind=_DEFAULT_ASYNC_TRANSPORT_KIND,
63
+ *,
64
+ client_options=None,
65
+ method_name=None,
66
+ is_streaming=False,
67
+ ):
68
+ """Wrap an async RPC method with common behavior.
69
+
70
+ Args:
71
+ func (Callable): The low-level async RPC method.
72
+ default_retry (Optional[google.api_core.retry_async.AsyncRetry]): The default
73
+ retry strategy. If ``None``, the method will not retry by default.
74
+ default_timeout (Optional[Union[google.api_core.timeout.Timeout, float]]): The
75
+ default timeout strategy. Can also be specified as an int or float. If
76
+ ``None``, the method will not have a timeout specified by default.
77
+ default_compression (Optional[grpc.Compression]): The default
78
+ grpc.Compression. If ``None``, the method will not have
79
+ compression specified by default.
80
+ client_info (Optional[google.api_core.gapic_v1.client_info.ClientInfo]):
81
+ Client information used to create a user-agent string that's
82
+ passed as gRPC metadata to the method. If unspecified, then
83
+ a sane default will be used. If ``None``, then no user agent
84
+ metadata will be provided to the RPC method.
85
+ kind (str): The transport kind for the RPC method. Defaults to "grpc_asyncio".
86
+ Allowed values for OpenTelemetry method tracing are "grpc_asyncio" and "rest_asyncio".
87
+ client_options
88
+ (Optional[google.api_core.client_options.ClientOptions]):
89
+ Client options used to configure client-level behavior, such as
90
+ custom OpenTelemetry tracer providers. Defaults to None.
91
+ method_name (Optional[str]): Optional explicit full RPC method name
92
+ (e.g. "/google.cloud.secretmanager.v1.SecretManagerService/AccessSecretVersion").
93
+ Used to identify the RPC for observability.
94
+ is_streaming (bool): Whether the RPC method is streaming. Defaults to False.
95
+ Streaming methods are currently gated and do not generate Tier 3 spans.
96
+
97
+ Returns:
98
+ Callable: A new callable that takes optional ``retry``, ``timeout``,
99
+ and ``compression`` arguments and applies the common error mapping,
100
+ retry, timeout, metadata, and compression behavior to the low-level RPC method.
101
+ """
102
+ if kind == _DEFAULT_ASYNC_TRANSPORT_KIND:
103
+ func = grpc_helpers_async.wrap_errors(func)
104
+
105
+ metadata = [client_info.to_grpc_metadata()] if client_info is not None else None
106
+
107
+ return functools.wraps(func)(
108
+ _AsyncGapicCallable(
109
+ func,
110
+ default_retry,
111
+ default_timeout,
112
+ default_compression,
113
+ metadata=metadata,
114
+ client_options=client_options,
115
+ method_name=method_name,
116
+ is_streaming=is_streaming,
117
+ client_info=client_info,
118
+ kind=kind,
119
+ )
120
+ )