Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
bfb40b0
feat(core): add request hook to inject GCP resource and project attri…
chalmerlowe Sep 3, 2026
87ca9b8
feat(core): implement complete T4 gRPC telemetry capture and response…
chalmerlowe Sep 9, 2026
451e17b
test(core): add comprehensive unit tests for T4 gRPC telemetry and hooks
chalmerlowe Sep 9, 2026
29de72e
refactor(core): adopt explicit _grpc_* naming for request extraction …
chalmerlowe Sep 9, 2026
7f6519f
test(core): align test names and assertions with _grpc_* naming conve…
chalmerlowe Sep 9, 2026
b0de0a4
feat(core): add url.domain, error attributes, and streamline T4 hooks
chalmerlowe Sep 10, 2026
ac095c5
feat(core): normalize gRPC span names and eliminate duplicate rpc.sys…
chalmerlowe Sep 10, 2026
ef7d77d
refactor(core): remove deferred gcp.resource.destination.id attribute
chalmerlowe Sep 10, 2026
aa2bead
feat(core): record rpc.response.status_code on wire attempt spans
chalmerlowe Sep 10, 2026
894b380
refactor(core): remove duplicate error attribute extraction in favor …
chalmerlowe Sep 10, 2026
1c1b9af
fix(observability): resolve mypy union-attr error and support environ…
chalmerlowe Sep 10, 2026
81b686c
refactor(observability): simplify response hook to record OK on succe…
chalmerlowe Sep 10, 2026
324b866
test(observability): cover request hook span edge cases for 100% bran…
chalmerlowe Sep 10, 2026
abeaf04
fix(observability): safely handle invalid port in endpoint attributes
chalmerlowe Sep 11, 2026
99a4d3d
fix(observability): ensure response hook only records OK on successfu…
chalmerlowe Sep 11, 2026
4b82c9c
refactor(observability): address review feedback on method name, url …
chalmerlowe Sep 11, 2026
0fb354a
docs(observability): clarify sync vs async behavior and specify semco…
chalmerlowe Sep 14, 2026
acce308
feat(gapic): add OpenTelemetry channel tracing to generator templates
chalmerlowe Sep 11, 2026
d337933
fix(gapic): resolve CI import errors on unreleased ClientInterceptor …
chalmerlowe Sep 11, 2026
13f1218
fix(gapic): use AnonymousCredentials in test_grpc_transport_channel_i…
chalmerlowe Sep 11, 2026
11ccd0d
test(gapic): update bazel integration goldens for otel channel tracing
chalmerlowe Sep 11, 2026
a7dad4f
ci(gapic): add OpenTelemetry test dependencies to showcase nox sessions
chalmerlowe Sep 14, 2026
858ff52
test(gapic): support client_options and otel interceptor in system te…
chalmerlowe Sep 14, 2026
b1c66e4
test(gapic): add showcase system test suite for OpenTelemetry channel…
chalmerlowe Sep 14, 2026
b409ba6
feat(gapic): broaden transport subclass check and harden tracing tests
chalmerlowe Sep 14, 2026
2059f32
refactor(gapic): guard ClientInterceptor under TYPE_CHECKING in trans…
chalmerlowe Sep 15, 2026
66a6f0e
test(gapic): synchronize NO COVER pragma in golden gRPC transports
chalmerlowe Sep 15, 2026
7ebebfa
test(gapic): support flexible import of construct_client in system tr…
chalmerlowe Sep 15, 2026
a7956a5
feat(gapic): resolve OTel interceptor in GrpcTransport and pass clien…
chalmerlowe Sep 15, 2026
cd5299a
test(gapic): update bazel integration goldens for transport tracing u…
chalmerlowe Sep 15, 2026
8b540c8
fix(gapic): resolve showcase mypy error and ensure base transport wra…
chalmerlowe Sep 16, 2026
880f5b9
feat(observability): add fallback status code and exception mapping f…
chalmerlowe Sep 16, 2026
9bb3305
test(gapic): harmonize showcase system tracing tests with env gating …
chalmerlowe Sep 16, 2026
af22028
fix(observability): ensure 100% branch coverage in error attribute ex…
chalmerlowe Sep 16, 2026
d073f2b
perf(generator): cache wrap_method tracing check at module level
chalmerlowe Sep 16, 2026
715f58b
refactor(observability): guard none span in response hook and tag int…
chalmerlowe Sep 16, 2026
c13f97a
feat(gapic): harden otel interceptor deduplication and options checki…
chalmerlowe Sep 16, 2026
b1b7ce4
test(gapic): update bazel integration goldens for interceptor hardening
chalmerlowe Sep 16, 2026
ce65620
Merge remote-tracking branch 'origin/main' into feat/otel-tracing-gap…
chalmerlowe Sep 16, 2026
d53607b
fix(ci): synchronize goldens with bazel generator and align with main
chalmerlowe Sep 16, 2026
f7f3feb
feat(gapic): wire method_name for mixin methods in base transport
chalmerlowe Sep 16, 2026
2f522bd
test(gapic): clarify test handling of abstract base transport NotImpl…
chalmerlowe Sep 17, 2026
ee5f778
refactor(testing): tighten fixture usage and standardize span asserti…
chalmerlowe Sep 17, 2026
89675ea
feat(observability): populate status.message span attribute for cross…
chalmerlowe Sep 17, 2026
8abdbc6
feat(observability): implement universal 4-path OpenTelemetry tracing
chalmerlowe Sep 21, 2026
cba76f3
fix(observability): resolve 4-path transport tracing gaps and support…
chalmerlowe Sep 21, 2026
2772ee3
fix(observability): resolve mypy typing, matrix coverage, and update …
chalmerlowe Sep 21, 2026
09080aa
fix(generator): isolate goldens from pre-commit formatting and sync i…
chalmerlowe Sep 21, 2026
a933acb
fix(generator): sync Bazel integration goldens with raw generator out…
chalmerlowe Sep 21, 2026
78a1db7
test(tracing): achieve 100% coverage on google-api-core and showcase …
chalmerlowe Sep 21, 2026
0f6e078
refactor(tracing): centralize wrap_method introspection and simplify …
chalmerlowe Sep 22, 2026
2535c2d
refactor(gapic-generator): unify transport method wrapping and centra…
chalmerlowe Sep 22, 2026
1f54064
refactor(observability): add trace_http_request helper and simplify r…
chalmerlowe Sep 22, 2026
4c8984d
refactor(gapic-generator): unify transport wrap helper, clean compat …
chalmerlowe Sep 22, 2026
03e8dff
fix(core): route async channel interceptors by RPC type
chalmerlowe Sep 22, 2026
f63234f
refactor(observability): complete docstring typing, enforce transport…
chalmerlowe Sep 22, 2026
2862cce
fix(observability): document fail-open rationale and cover tracing in…
chalmerlowe Sep 23, 2026
b621fcd
feat(observability): align REST rpc.system.name, cache trace context …
chalmerlowe Sep 23, 2026
8382184
fix(generator): add coverage pragmas to _compat observability fallbac…
chalmerlowe Sep 23, 2026
c22f65b
test(core): cover apply_channel_interceptors fallback branches
chalmerlowe Sep 23, 2026
b50c26e
test(core): cover all non-appendable branches in apply_channel_interc…
chalmerlowe Sep 23, 2026
84ff4d9
test(generator): add semantic span contracts and 18-feature complianc…
chalmerlowe Sep 23, 2026
84a2128
refactor(generator): consolidate telemetry compliance scenarios and p…
chalmerlowe Sep 23, 2026
2f8d713
refactor(observability): address review comments on HTTP span context…
chalmerlowe Sep 24, 2026
c148624
refactor(observability): collapse _HttpSpanContext into class trace_h…
chalmerlowe Sep 24, 2026
4d2c361
style(generator): move TYPE_CHECKING import to bottom of imports in g…
chalmerlowe Sep 24, 2026
40b0db6
test(generator): remove pragma: NO COVER from base transport fallback…
chalmerlowe Sep 24, 2026
d535d6d
refactor(gapic-generator): inline _wrap helper directly into _wrap_me…
chalmerlowe Sep 24, 2026
86e2eed
docs(gapic-generator): add docstrings to transport method wrappers an…
chalmerlowe Sep 24, 2026
7934816
test(showcase): add F3 live retry hierarchy and aggregation complianc…
chalmerlowe Sep 24, 2026
bc39993
refactor(testing): drive telemetry compliance test suite from CSV fea…
chalmerlowe Sep 24, 2026
f2c08f9
feat(testing): add --dump-spans option to dump raw OpenTelemetry JSON…
chalmerlowe Sep 24, 2026
b2a97ed
refactor(testing): output telemetry compliance scorecard in clean CSV…
chalmerlowe Sep 24, 2026
f8ed46d
fix(observability): resolve mypy exit-return typing and sync bazel in…
chalmerlowe Sep 24, 2026
566e55d
fix(testing): simplify span_contract record and support local core in…
chalmerlowe Sep 25, 2026
a4dd85a
ci(generator): add showcase-system test workflow
chalmerlowe Sep 25, 2026
96e54eb
fix(generator): include sequence.proto in showcase library generation
chalmerlowe Sep 25, 2026
79b01ba
ci(generator): run full showcase system test suite in CI
chalmerlowe Sep 25, 2026
df0617e
ci(generator): enable verbose and uncaptured output for showcase syst…
chalmerlowe Sep 25, 2026
7599f5b
refactor(testing): clear legacy span_contract boilerplate for spec-dr…
chalmerlowe Sep 25, 2026
8b07759
docs(testing): update telemetry requirements matrix with concrete 1-t…
chalmerlowe Sep 25, 2026
4296316
feat(generator): implement clean-slate spec-driven telemetry complian…
chalmerlowe Sep 25, 2026
178686f
refactor(testing): remove deprecated span_contract references from te…
chalmerlowe Sep 25, 2026
6bc5406
refactor(testing): make matrix sole authority for sequence and retry …
chalmerlowe Sep 25, 2026
bc3a140
refactor(testing): use monkeypatch for tracing environment variable c…
chalmerlowe Sep 25, 2026
b6df0b7
refactor(testing): introduce construct_observability_client helper fo…
chalmerlowe Sep 25, 2026
02cb934
refactor(testing): remove unnecessary use_mtls plumbing from test run…
chalmerlowe Sep 25, 2026
38311d5
refactor(testing): inject clients into scenario runners and centraliz…
chalmerlowe Sep 25, 2026
fb3c3ff
refactor(testing): eliminate magic numbers and unify sequence executi…
chalmerlowe Sep 25, 2026
2bba42d
refactor(testing): decompose assert_span_matches_row into modular sub…
chalmerlowe Sep 25, 2026
0d3dcdf
docs(testing): enrich architectural docstrings and commentary across …
chalmerlowe Sep 25, 2026
89c6f86
refactor(testing): dry up span metadata assertions and simplify DSL d…
chalmerlowe Sep 25, 2026
92f9a2f
fix(core): remove unreachable branch in trace_http_request.__exit__ t…
chalmerlowe Sep 25, 2026
b640ac8
test(observability): harden unit test boundaries, parametrization, an…
chalmerlowe Sep 25, 2026
fe2b289
chore: merge branch 'main' of https://github.com/googleapis/google-cl…
chalmerlowe Sep 25, 2026
d20d306
fix(core): remove unreachable branch in trace_http_request.__exit__ t…
chalmerlowe Sep 28, 2026
6cca78d
chore(spanner): align samples noxfile imports with librarian generate
chalmerlowe Sep 28, 2026
26410c8
test(showcase): install local google-api-core in showcase_w_rest_asyn…
chalmerlowe Sep 28, 2026
6da4d08
chore(gapic-generator): document coverage pragmas and lifecycles on t…
chalmerlowe Sep 28, 2026
1a868df
test(gapic-generator): skip positive tracing tests when observability…
chalmerlowe Sep 28, 2026
0c28765
chore: restore unrelated packages to match origin/main
chalmerlowe Sep 28, 2026
da8b1d0
chore(ci): restore gapic-generator-tests.yml to match origin/main
chalmerlowe Sep 28, 2026
0f6bd43
chore(generator): restore test_streams.py formatting to match origin/…
chalmerlowe Sep 28, 2026
c90fab0
chore: restore .gitignore to match origin/main
chalmerlowe Sep 28, 2026
302e717
chore(testing): remove obsolete --dump-spans option from showcase sys…
chalmerlowe Sep 28, 2026
1a79526
refactor(core): use explicit transport kind constants and align metho…
chalmerlowe Sep 28, 2026
265359b
refactor(gapic_v1): privatize transport kind constants in method and …
chalmerlowe Sep 28, 2026
2aad17b
refactor(tracing): refine span exception filtering, inherit async cal…
chalmerlowe Sep 29, 2026
1b0c4c9
refactor(tracing): encapsulate HTTP wire tracing into _TraceContext a…
chalmerlowe Sep 29, 2026
ed802b8
fix(tracing): support BaseException in _extract_error_attributes for …
chalmerlowe Sep 29, 2026
775d71e
docs(tracing): enrich _fallback_apply_interceptors docstrings and pat…
chalmerlowe Sep 29, 2026
639aa7e
fix(generator): preserve transport kind in wrap_method fallback on ol…
chalmerlowe Sep 29, 2026
89e0a9c
fix(tracing): gate kind injection in base transport, clean unused imp…
chalmerlowe Sep 29, 2026
d6632eb
fix(generator): add 3-era google-api-core support to async transport …
chalmerlowe Sep 29, 2026
3358ec3
fix(observability): resolve showcase test coverage and compat golden …
chalmerlowe Sep 29, 2026
fd86f66
chore: merge branch 'main' into feat/otel-tracing-universal-4path
chalmerlowe Sep 29, 2026
a26f127
refactor(observability): inline error recording into _TraceContext an…
chalmerlowe Sep 30, 2026
5b82add
test(showcase): upload raw spans artifact in CI and print compliance …
chalmerlowe Sep 30, 2026
2f48184
test(showcase): pin upload-artifact action and capture error states i…
chalmerlowe Sep 30, 2026
a8a8c64
ci(generator): gate local core install and add showcase canary job
chalmerlowe Sep 30, 2026
048cf39
fix(ci): gate span tests on _TraceContext and exclude broken gcp-sphi…
chalmerlowe Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 67 additions & 6 deletions .github/workflows/gapic-generator-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
with:
python-version: "${{ matrix.python }}"
allow-prereleases: true
# Caches compiled wheels locally to prevent building heavy libraries
# Caches compiled wheels locally to prevent building heavy libraries
# such as grpcio, which we build from scratch on every run for Python 3.15+.
# Follow https://github.com/grpc/grpc/issues/41010 for updates.
cache: 'pip'
Expand Down Expand Up @@ -142,6 +142,68 @@ jobs:
run: |
cd packages/gapic-generator
nox -s ${{ matrix.target }}-${MATRIX_PYTHON}
- name: Upload Raw Spans Artifact
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: raw-spans-${{ matrix.target }}-${{ matrix.python }}${{ matrix.logging_scope && format('-{0}', matrix.logging_scope) || '' }}
path: packages/gapic-generator/tests/system/raw_spans_output.json
if-no-files-found: ignore

showcase-canary:
needs: python_config
strategy:
# Run canary tests with local monorepo google-api-core on bookend runtimes
matrix:
python: ["${{ needs.python_config.outputs.oldest_python }}", "${{ needs.python_config.outputs.latest_stable_python }}"]
target: [showcase, showcase_w_rest_async]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python "${{ matrix.python }}"
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "${{ matrix.python }}"
cache: 'pip'
- name: Install system dependencies.
run: |
sudo apt-get update
sudo apt-get install -y curl pandoc unzip
- name: Install showcase
run: |
sudo mkdir -p /usr/src/showcase
sudo chown -R ${USER} /usr/src/
curl --location https://github.com/googleapis/gapic-showcase/releases/download/v${SHOWCASE_VERSION}/gapic-showcase-${SHOWCASE_VERSION}-linux-amd64.tar.gz --output /usr/src/showcase/showcase-${SHOWCASE_VERSION}-linux-amd64.tar.gz
cd /usr/src/showcase/
tar -xf showcase-*
./gapic-showcase run &
cd -
- name: Install nox.
run: python -m pip install nox
- name: Install protoc
run: |
sudo mkdir -p /usr/src/protoc/
sudo chown -R ${USER} /usr/src/
curl --location https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip --output /usr/src/protoc/protoc-${PROTOC_VERSION}.zip
cd /usr/src/protoc/
unzip protoc-${PROTOC_VERSION}.zip
sudo ln -s /usr/src/protoc/bin/protoc /usr/local/bin/protoc
- name: Run showcase canary with local google-api-core.
env:
INSTALL_LOCAL_CORE: "true"
MATRIX_PYTHON: ${{ matrix.python }}
run: |
cd packages/gapic-generator
nox -s ${{ matrix.target }}-${MATRIX_PYTHON}
- name: Upload Raw Spans Artifact
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: raw-spans-canary-${{ matrix.target }}-${{ matrix.python }}
path: packages/gapic-generator/tests/system/raw_spans_output.json
if-no-files-found: ignore

showcase-mypy:
needs: python_config
Expand Down Expand Up @@ -210,7 +272,7 @@ jobs:
with:
python-version: ${{ needs.python_config.outputs.prerelease_python }}
allow-prereleases: true
# Caches compiled wheels locally to prevent building heavy libraries
# Caches compiled wheels locally to prevent building heavy libraries
# such as grpcio, which we build from scratch on every run for Python 3.15+.
# Follow https://github.com/grpc/grpc/issues/41010 for updates.
cache: 'pip'
Expand Down Expand Up @@ -242,7 +304,7 @@ jobs:
with:
python-version: ${{ matrix.python }}
allow-prereleases: true
# Caches compiled wheels locally to prevent building heavy libraries
# Caches compiled wheels locally to prevent building heavy libraries
# such as grpcio, which we build from scratch on every run for Python 3.15+.
# Follow https://github.com/grpc/grpc/issues/41010 for updates.
cache: 'pip'
Expand Down Expand Up @@ -287,13 +349,13 @@ jobs:

- name: Run Bazel Integration Tests
run: |
# We need to move into the package directory if the
# We need to move into the package directory if the
# WORKSPACE file is located there.
cd packages/gapic-generator
bazelisk test //tests/integration/... --test_output=errors

# Final job to simplify branch protection rules.
# This job will be reported as 'Success' if all its dependencies either
# This job will be reported as 'Success' if all its dependencies either
# pass or are skipped due to path filtering.
status:
needs: [check_changes, python_config, showcase-unit, showcase, showcase-mypy, goldens, goldens-prerelease, fragment-snippet, integration]
Expand All @@ -314,4 +376,3 @@ jobs:
exit 1
fi
echo "All checks passed or were successfully skipped."

1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: '^packages/gapic-generator/tests/integration/goldens/'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
Expand Down
3 changes: 3 additions & 0 deletions ci/get_package_shards.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,11 @@
# extra name 'mssql-pymssql' under strict uv PEP 621 parsing in sqlalchemy==2.1.0rc2,
# pulled via global UV_PRERELEASE=allow). Awaiting team feedback on a long-term
# solution (e.g. package migration out of the monorepo or adjusting workflow settings).
# NOTE: 'gcp-sphinx-docfx-yaml' is temporarily excluded due to legacy build dependencies
# (unify/untokenize) failing to build under modern setuptools/uv.
EXCLUDED_PACKAGES = {
"sqlalchemy-bigquery",
"gcp-sphinx-docfx-yaml",
}


Expand Down
10 changes: 10 additions & 0 deletions packages/gapic-generator/gapic/schema/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,50 +19,60 @@
"DeleteOperation",
request_type="operations_pb2.DeleteOperationRequest",
response_type="None",
rpc_name="google.longrunning.Operations/DeleteOperation",

@chalmerlowe chalmerlowe Sep 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

As context for the reviewer:

For native methods (like Echo or GetSecret), the generator reads the service's .proto file directly, so constructing the name in the template is straightforward:

method_name="{{ '.'.join(method.meta.address.package) }}.{{ service.name }}/{{ method.name }}"

However, mixins don't live in the service’s proto. Mixin methods (GetOperation, GetIamPolicy, ListLocations) are synthetic—they are injected by the generator from the static catalog in gapic/schema/mixins.py

Without a name attribute, any mixin call (like polling an operation or checking IAM permissions) would be unable to start an OpenTelemetry method span, or would emit an unknown/nameless span that failed our contract checks.

),
"WaitOperation": wrappers.MixinMethod(
"WaitOperation",
request_type="operations_pb2.WaitOperationRequest",
response_type="operations_pb2.Operation",
rpc_name="google.longrunning.Operations/WaitOperation",
Comment thread
chalmerlowe marked this conversation as resolved.
),
"ListOperations": wrappers.MixinMethod(
"ListOperations",
request_type="operations_pb2.ListOperationsRequest",
response_type="operations_pb2.ListOperationsResponse",
rpc_name="google.longrunning.Operations/ListOperations",
),
"CancelOperation": wrappers.MixinMethod(
"CancelOperation",
request_type="operations_pb2.CancelOperationRequest",
response_type="None",
rpc_name="google.longrunning.Operations/CancelOperation",
),
"GetOperation": wrappers.MixinMethod(
"GetOperation",
request_type="operations_pb2.GetOperationRequest",
response_type="operations_pb2.Operation",
rpc_name="google.longrunning.Operations/GetOperation",
),
"TestIamPermissions": wrappers.MixinMethod(
"TestIamPermissions",
request_type="iam_policy_pb2.TestIamPermissionsRequest",
response_type="iam_policy_pb2.TestIamPermissionsResponse",
rpc_name="google.iam.v1.IAMPolicy/TestIamPermissions",
),
"GetIamPolicy": wrappers.MixinMethod(
"GetIamPolicy",
request_type="iam_policy_pb2.GetIamPolicyRequest",
response_type="policy_pb2.Policy",
rpc_name="google.iam.v1.IAMPolicy/GetIamPolicy",
),
"SetIamPolicy": wrappers.MixinMethod(
"SetIamPolicy",
request_type="iam_policy_pb2.SetIamPolicyRequest",
response_type="policy_pb2.Policy",
rpc_name="google.iam.v1.IAMPolicy/SetIamPolicy",
),
"ListLocations": wrappers.MixinMethod(
"ListLocations",
request_type="locations_pb2.ListLocationsRequest",
response_type="locations_pb2.ListLocationsResponse",
rpc_name="google.cloud.location.Locations/ListLocations",
),
"GetLocation": wrappers.MixinMethod(
"GetLocation",
request_type="locations_pb2.GetLocationRequest",
response_type="locations_pb2.Location",
rpc_name="google.cloud.location.Locations/GetLocation",
),
}
2 changes: 2 additions & 0 deletions packages/gapic-generator/gapic/schema/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,8 @@ class MixinMethod:
name: str
request_type: str
response_type: str
rpc_name: str = ""



@dataclasses.dataclass(frozen=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
"""A compatibility module for older versions of google-api-core."""
{% set has_auto_populated_fields = api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %}
{# TODO(https://github.com/googleapis/google-cloud-python/issues/17813):
Clean up this file/functions when the minimum supported version of
Clean up this file/functions when the minimum supported version of
google-api-core has the functions in `_compat.py.j2`. #}
{# TODO(https://github.com/googleapis/google-cloud-python/issues/17884):
Add conditional logic to check if static code exists in google-api-core and use it from there,
{# TODO(https://github.com/googleapis/google-cloud-python/issues/17884):
Add conditional logic to check if static code exists in google-api-core and use it from there,
falling back to the local implementation if not present. #}
{# TODO(https://github.com/googleapis/google-cloud-python/issues/17883):
{# TODO(https://github.com/googleapis/google-cloud-python/issues/17883):
Backfill compatibility functions being removed from the client layer. #}

import os
Expand All @@ -20,12 +20,9 @@ import uuid
import google.protobuf.message
{% endif %}

from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional, Sequence, Tuple
{% if has_auto_populated_fields %}
from typing import TYPE_CHECKING, Union

if TYPE_CHECKING: # pragma: NO COVER
import proto # type: ignore[import-untyped]
{% endif %}

from google.api_core import path_template
Expand All @@ -34,9 +31,117 @@ from google.auth.exceptions import MutualTLSChannelError
from google.protobuf import json_format
from urllib.parse import urlparse, urlunparse

{% if has_auto_populated_fields %}
if TYPE_CHECKING: # pragma: NO COVER
import proto # type: ignore[import-untyped]
{% endif %}

# The _observability module was introduced in google-api-core 2.36.0+.
# On older versions of google-api-core or when type-checking against them,
# mypy may flag attr-defined or assignment errors when fallback to None occurs.
try:
from google.api_core import _observability # type: ignore[attr-defined]
except ImportError: # pragma: NO COVER
_observability = None # type: ignore[assignment]

if _observability is not None and hasattr(_observability, "trace_http_request"):
trace_http_request = _observability.trace_http_request
else: # pragma: NO COVER
# Fallback for older versions of google-api-core without HTTP tracing.
class _FallbackTraceContext:
def __enter__(self) -> "_FallbackTraceContext":
return self

def __exit__(self, *args: Any) -> None:
pass

def record_response(self, response: Any) -> None:
pass

record_http_response = record_response

def record_error(self, exc: BaseException | None) -> None:
pass

record_http_error = record_error

def trace_http_request(*args: Any, **kwargs: Any) -> _FallbackTraceContext:
return _FallbackTraceContext()

try:
from google.api_core import grpc_helpers_async
except ImportError: # pragma: NO COVER
grpc_helpers_async = None # type: ignore[assignment]


def _fallback_apply_interceptors(
channel: Any, interceptors: Optional[Sequence[Any]] = None
) -> Any: # pragma: NO COVER
"""Fallback helper to attach interceptors to an existing async gRPC channel.

Synchronous gRPC channels allow post-creation wrapping via
`grpc.intercept_channel(channel, *interceptors)`. However, asynchronous
channels (`grpc.aio.Channel`) in grpcio are immutable post-creation and
normally require interceptors at initialization.

When generated client libraries run against older versions of `google-api-core`
(< 2.36.0) where `grpc_helpers_async.apply_channel_interceptors` is not yet
available, this fallback mutates the channel's internal interceptor lists
directly so telemetry and auth interceptors remain operational.

Args:
channel (Any): The gRPC channel instance (typically a `grpc.aio.Channel`
or mock).
interceptors (Optional[Sequence[Any]]): A sequence of gRPC interceptor
instances or callables to register on the channel.

Returns:
Any: The channel instance with interceptors registered.
"""
mapping = (
("intercept_unary_unary", "_unary_unary_interceptors"),
("intercept_unary_stream", "_unary_stream_interceptors"),
("intercept_stream_unary", "_stream_unary_interceptors"),
("intercept_stream_stream", "_stream_stream_interceptors"),
)
if interceptors:
for interceptor in interceptors:
matched = False

# Path 1: Interface-matched routing.
# Inspect whether the interceptor implements specific gRPC method hooks
# (unary-unary, unary-stream, stream-unary, stream-stream). If the channel
# maintains a corresponding internal list, append the interceptor (preventing duplicates).
# A single interceptor can handle multiple call patterns, so check all mappings.
for method_name, attr_name in mapping:
if hasattr(interceptor, method_name) and hasattr(channel, attr_name):
target_list = getattr(channel, attr_name)
if isinstance(target_list, list) and interceptor not in target_list:
target_list.append(interceptor)
matched = True

# Path 2: Duck-typed / callable fallback.
# If the interceptor did not implement any of the specific method signatures
# above (e.g., generic callables, untyped wrappers, or test doubles), fall back
# to registering it in the primary unary-unary interceptor list if present.
if not matched and hasattr(channel, "_unary_unary_interceptors"):
unary_interceptors = channel._unary_unary_interceptors
if isinstance(unary_interceptors, list) and interceptor not in unary_interceptors:
unary_interceptors.append(interceptor)
return channel


if grpc_helpers_async is not None and hasattr(
grpc_helpers_async, "apply_channel_interceptors"
):
apply_channel_interceptors = grpc_helpers_async.apply_channel_interceptors # pragma: NO COVER
else: # pragma: NO COVER
apply_channel_interceptors = _fallback_apply_interceptors


try:
# note: `#type: ignore` is added because the return type for `should_use_client_cert`
# is different than that of the fallback implementation below. This will be removed once
# is different than that of the fallback implementation below. This will be removed once
# we bump the minimum supported version of google-auth.
from google.auth.transport.mtls import should_use_client_cert # type: ignore
except ImportError: # pragma: NO COVER
Expand Down
Loading
Loading