Skip to content
This repository was archived by the owner on Mar 31, 2026. It is now read-only.

Commit d953dc8

Browse files
committed
chore: align async support with upstream/main after rebase
- Port TLS/mTLS and experimental host support to AsyncClient - Port enable_interceptors_in_tests to AsyncInstance.database - Regenerate synchronous code via CrossSync - Fix noxfile.py for pytest-asyncio compatibility and test isolation - Add comprehensive asynchronous system tests
1 parent 9192b4f commit d953dc8

28 files changed

+631
-135
lines changed

google/cloud/spanner_v1/_async/_helpers.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,67 @@ async def _retry(
5151
before_next_retry(retries, delay)
5252
await asyncio.sleep(delay)
5353
retries += 1
54+
55+
56+
def _create_experimental_host_transport(
57+
transport_factory,
58+
experimental_host,
59+
use_plain_text,
60+
ca_certificate,
61+
client_certificate,
62+
client_key,
63+
interceptors=None,
64+
):
65+
"""Creates an experimental host transport for Spanner in async mode.
66+
67+
Args:
68+
transport_factory (type): The transport class to instantiate (e.g.
69+
`SpannerGrpcAsyncIOTransport`).
70+
experimental_host (str): The endpoint for the experimental host.
71+
use_plain_text (bool): Whether to use a plain text (insecure) connection.
72+
ca_certificate (str): Path to the CA certificate file for TLS.
73+
client_certificate (str): Path to the client certificate file for mTLS.
74+
client_key (str): Path to the client key file for mTLS.
75+
interceptors (list): Optional list of interceptors to add to the channel.
76+
77+
Returns:
78+
object: An instance of the transport class created by `transport_factory`.
79+
80+
Raises:
81+
ValueError: If TLS/mTLS configuration is invalid.
82+
"""
83+
import grpc.aio
84+
from google.auth.credentials import AnonymousCredentials
85+
86+
channel = None
87+
if use_plain_text:
88+
channel = grpc.aio.insecure_channel(
89+
target=experimental_host, interceptors=interceptors
90+
)
91+
elif ca_certificate:
92+
with open(ca_certificate, "rb") as f:
93+
ca_cert = f.read()
94+
if client_certificate and client_key:
95+
with open(client_certificate, "rb") as f:
96+
client_cert = f.read()
97+
with open(client_key, "rb") as f:
98+
private_key = f.read()
99+
ssl_creds = grpc.ssl_channel_credentials(
100+
root_certificates=ca_cert,
101+
private_key=private_key,
102+
certificate_chain=client_cert,
103+
)
104+
elif client_certificate or client_key:
105+
raise ValueError(
106+
"Both client_certificate and client_key must be provided for mTLS connection"
107+
)
108+
else:
109+
ssl_creds = grpc.ssl_channel_credentials(root_certificates=ca_cert)
110+
channel = grpc.aio.secure_channel(
111+
experimental_host, ssl_creds, interceptors=interceptors
112+
)
113+
else:
114+
raise ValueError(
115+
"TLS/mTLS connection requires ca_certificate to be set for experimental_host"
116+
)
117+
return transport_factory(channel=channel, credentials=AnonymousCredentials())

google/cloud/spanner_v1/_async/client.py

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ def __init__(
270270
default_transaction_options: Optional[DefaultTransactionOptions] = None,
271271
experimental_host=None,
272272
disable_builtin_metrics=False,
273+
use_plain_text=False,
274+
ca_certificate=None,
275+
client_certificate=None,
276+
client_key=None,
273277
):
274278
self._emulator_host = _get_spanner_emulator_host()
275279
self._experimental_host = experimental_host
@@ -284,6 +288,12 @@ def __init__(
284288
if self._emulator_host:
285289
credentials = AnonymousCredentials()
286290
elif self._experimental_host:
291+
# For all experimental host endpoints project is default
292+
project = "default"
293+
self._use_plain_text = use_plain_text
294+
self._ca_certificate = ca_certificate
295+
self._client_certificate = client_certificate
296+
self._client_key = client_key
287297
credentials = AnonymousCredentials()
288298
elif isinstance(credentials, AnonymousCredentials):
289299
self._emulator_host = self._client_options.api_endpoint
@@ -382,11 +392,31 @@ def instance_admin_api(self):
382392
transport=transport,
383393
)
384394
elif self._experimental_host:
395+
from google.cloud.spanner_v1._helpers import (
396+
_create_experimental_host_transport as _create_experimental_host_transport_sync,
397+
)
398+
from google.cloud.spanner_v1._async._helpers import (
399+
_create_experimental_host_transport as _create_experimental_host_transport_async,
400+
)
401+
385402
if CrossSync.is_async:
386-
channel = grpc.aio.insecure_channel(self._experimental_host)
403+
transport = _create_experimental_host_transport_async(
404+
InstanceAdminGrpcTransport,
405+
self._experimental_host,
406+
self._use_plain_text,
407+
self._ca_certificate,
408+
self._client_certificate,
409+
self._client_key,
410+
)
387411
else:
388-
channel = grpc.insecure_channel(self._experimental_host)
389-
transport = InstanceAdminGrpcTransport(channel=channel)
412+
transport = _create_experimental_host_transport_sync(
413+
InstanceAdminGrpcTransport,
414+
self._experimental_host,
415+
self._use_plain_text,
416+
self._ca_certificate,
417+
self._client_certificate,
418+
self._client_key,
419+
)
390420
self._instance_admin_api = InstanceAdminClient(
391421
client_info=self._client_info,
392422
client_options=self._client_options,
@@ -416,11 +446,31 @@ def database_admin_api(self):
416446
transport=transport,
417447
)
418448
elif self._experimental_host:
449+
from google.cloud.spanner_v1._helpers import (
450+
_create_experimental_host_transport as _create_experimental_host_transport_sync,
451+
)
452+
from google.cloud.spanner_v1._async._helpers import (
453+
_create_experimental_host_transport as _create_experimental_host_transport_async,
454+
)
455+
419456
if CrossSync.is_async:
420-
channel = grpc.aio.insecure_channel(self._experimental_host)
457+
transport = _create_experimental_host_transport_async(
458+
DatabaseAdminGrpcTransport,
459+
self._experimental_host,
460+
self._use_plain_text,
461+
self._ca_certificate,
462+
self._client_certificate,
463+
self._client_key,
464+
)
421465
else:
422-
channel = grpc.insecure_channel(self._experimental_host)
423-
transport = DatabaseAdminGrpcTransport(channel=channel)
466+
transport = _create_experimental_host_transport_sync(
467+
DatabaseAdminGrpcTransport,
468+
self._experimental_host,
469+
self._use_plain_text,
470+
self._ca_certificate,
471+
self._client_certificate,
472+
self._client_key,
473+
)
424474
self._database_admin_api = DatabaseAdminClient(
425475
client_info=self._client_info,
426476
client_options=self._client_options,

google/cloud/spanner_v1/_async/database.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -472,13 +472,31 @@ def spanner_api(self):
472472
)
473473
return self._spanner_api
474474
if self._instance.experimental_host is not None:
475+
from google.cloud.spanner_v1._helpers import (
476+
_create_experimental_host_transport as _create_experimental_host_transport_sync,
477+
)
478+
from google.cloud.spanner_v1._async._helpers import (
479+
_create_experimental_host_transport as _create_experimental_host_transport_async,
480+
)
481+
475482
if CrossSync.is_async:
476-
channel = grpc.aio.insecure_channel(
477-
self._instance.experimental_host
483+
transport = _create_experimental_host_transport_async(
484+
SpannerGrpcTransport,
485+
self._instance.experimental_host,
486+
self._instance._client._use_plain_text,
487+
self._instance._client._ca_certificate,
488+
self._instance._client._client_certificate,
489+
self._instance._client._client_key,
478490
)
479491
else:
480-
channel = grpc.insecure_channel(self._instance.experimental_host)
481-
transport = SpannerGrpcTransport(channel=channel)
492+
transport = _create_experimental_host_transport_sync(
493+
SpannerGrpcTransport,
494+
self._instance.experimental_host,
495+
self._instance._client._use_plain_text,
496+
self._instance._client._ca_certificate,
497+
self._instance._client._client_certificate,
498+
self._instance._client._client_key,
499+
)
482500
self._spanner_api = SpannerClient(
483501
client_info=client_info,
484502
transport=transport,

google/cloud/spanner_v1/_async/database_sessions_manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
"""Manage sessions for a database."""
16+
__CROSS_SYNC_OUTPUT__ = "google.cloud.spanner_v1.database_sessions_manager"
1617

1718
from datetime import timedelta
1819
from enum import Enum

google/cloud/spanner_v1/_async/testing/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)