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

Commit 124c0f7

Browse files
committed
feat(async_grpc_client): add client_info support to AsyncGrpcClient for user agent customization
1 parent 2e1a1eb commit 124c0f7

2 files changed

Lines changed: 66 additions & 12 deletions

File tree

google/cloud/storage/_experimental/asyncio/async_grpc_client.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
"""An async client for interacting with Google Cloud Storage using the gRPC API."""
1616

1717
from google.cloud import _storage_v2 as storage_v2
18+
from google.cloud._storage_v2.services.storage.transports.base import (
19+
DEFAULT_CLIENT_INFO,
20+
)
1821

1922

2023
class AsyncGrpcClient:
@@ -39,7 +42,8 @@ class AsyncGrpcClient:
3942
(Optional) Whether to attempt to use DirectPath for gRPC connections.
4043
Defaults to ``True``.
4144
"""
42-
45+
# current scenario: Whatever client_info the users send , you're sending then to end user.
46+
# ie. None.
4347
def __init__(
4448
self,
4549
credentials=None,
@@ -65,8 +69,15 @@ def _create_async_grpc_client(
6569
transport_cls = storage_v2.StorageAsyncClient.get_transport_class(
6670
"grpc_asyncio"
6771
)
72+
73+
if client_info is None:
74+
client_info = DEFAULT_CLIENT_INFO
75+
primary_user_agent = client_info.to_user_agent()
76+
6877
channel = transport_cls.create_channel(
69-
attempt_direct_path=attempt_direct_path, credentials=credentials
78+
attempt_direct_path=attempt_direct_path,
79+
credentials=credentials,
80+
options=(("grpc.primary_user_agent", primary_user_agent),),
7081
)
7182
transport = transport_cls(channel=channel)
7283

tests/unit/asyncio/test_async_grpc_client.py

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
from unittest import mock
1717
from google.auth import credentials as auth_credentials
1818
from google.auth.credentials import AnonymousCredentials
19+
from google.api_core import client_info as client_info_lib
20+
from google.cloud.storage._experimental.asyncio import async_grpc_client
21+
from google.cloud.storage._experimental.asyncio.async_grpc_client import (
22+
DEFAULT_CLIENT_INFO,
23+
)
1924

2025

2126
def _make_credentials(spec=None):
@@ -27,32 +32,58 @@ def _make_credentials(spec=None):
2732
class TestAsyncGrpcClient(unittest.TestCase):
2833
@mock.patch("google.cloud._storage_v2.StorageAsyncClient")
2934
def test_constructor_default_options(self, mock_async_storage_client):
30-
from google.cloud.storage._experimental.asyncio import async_grpc_client
3135

3236
mock_transport_cls = mock.MagicMock()
3337
mock_async_storage_client.get_transport_class.return_value = mock_transport_cls
3438
mock_creds = _make_credentials()
3539

3640
async_grpc_client.AsyncGrpcClient(credentials=mock_creds)
3741

42+
primary_user_agent = DEFAULT_CLIENT_INFO.to_user_agent()
43+
expected_options = (("grpc.primary_user_agent", primary_user_agent),)
44+
3845
mock_async_storage_client.get_transport_class.assert_called_once_with(
3946
"grpc_asyncio"
4047
)
4148
mock_transport_cls.create_channel.assert_called_once_with(
42-
attempt_direct_path=True, credentials=mock_creds
49+
attempt_direct_path=True,
50+
credentials=mock_creds,
51+
options=expected_options,
4352
)
4453
mock_channel = mock_transport_cls.create_channel.return_value
4554
mock_transport_cls.assert_called_once_with(channel=mock_channel)
4655
mock_transport = mock_transport_cls.return_value
4756
mock_async_storage_client.assert_called_once_with(
4857
transport=mock_transport,
4958
client_options=None,
50-
client_info=None,
59+
client_info=DEFAULT_CLIENT_INFO,
60+
)
61+
62+
@mock.patch("google.cloud._storage_v2.StorageAsyncClient")
63+
def test_constructor_with_client_info(self, mock_async_storage_client):
64+
65+
mock_transport_cls = mock.MagicMock()
66+
mock_async_storage_client.get_transport_class.return_value = mock_transport_cls
67+
mock_creds = _make_credentials()
68+
client_info = client_info_lib.ClientInfo(
69+
client_library_version="1.2.3",
70+
)
71+
72+
async_grpc_client.AsyncGrpcClient(
73+
credentials=mock_creds, client_info=client_info
74+
)
75+
76+
primary_user_agent = client_info.to_user_agent()
77+
expected_options = (("grpc.primary_user_agent", primary_user_agent),)
78+
79+
mock_transport_cls.create_channel.assert_called_once_with(
80+
attempt_direct_path=True,
81+
credentials=mock_creds,
82+
options=expected_options,
5183
)
5284

5385
@mock.patch("google.cloud._storage_v2.StorageAsyncClient")
5486
def test_constructor_disables_directpath(self, mock_async_storage_client):
55-
from google.cloud.storage._experimental.asyncio import async_grpc_client
5687

5788
mock_transport_cls = mock.MagicMock()
5889
mock_async_storage_client.get_transport_class.return_value = mock_transport_cls
@@ -62,15 +93,19 @@ def test_constructor_disables_directpath(self, mock_async_storage_client):
6293
credentials=mock_creds, attempt_direct_path=False
6394
)
6495

96+
primary_user_agent = DEFAULT_CLIENT_INFO.to_user_agent()
97+
expected_options = (("grpc.primary_user_agent", primary_user_agent),)
98+
6599
mock_transport_cls.create_channel.assert_called_once_with(
66-
attempt_direct_path=False, credentials=mock_creds
100+
attempt_direct_path=False,
101+
credentials=mock_creds,
102+
options=expected_options,
67103
)
68104
mock_channel = mock_transport_cls.create_channel.return_value
69105
mock_transport_cls.assert_called_once_with(channel=mock_channel)
70106

71107
@mock.patch("google.cloud._storage_v2.StorageAsyncClient")
72108
def test_grpc_client_property(self, mock_grpc_gapic_client):
73-
from google.cloud.storage._experimental.asyncio import async_grpc_client
74109

75110
# Arrange
76111
mock_transport_cls = mock.MagicMock()
@@ -81,7 +116,8 @@ def test_grpc_client_property(self, mock_grpc_gapic_client):
81116
mock_transport_cls.return_value = mock.sentinel.transport
82117

83118
mock_creds = _make_credentials()
84-
mock_client_info = mock.sentinel.client_info
119+
mock_client_info = mock.MagicMock(spec=client_info_lib.ClientInfo)
120+
mock_client_info.to_user_agent.return_value = "test-user-agent"
85121
mock_client_options = mock.sentinel.client_options
86122
mock_attempt_direct_path = mock.sentinel.attempt_direct_path
87123

@@ -102,8 +138,11 @@ def test_grpc_client_property(self, mock_grpc_gapic_client):
102138
retrieved_client = client.grpc_client
103139

104140
# Assert
141+
expected_options = (("grpc.primary_user_agent", "test-user-agent"),)
105142
mock_transport_cls.create_channel.assert_called_once_with(
106-
attempt_direct_path=mock_attempt_direct_path, credentials=mock_creds
143+
attempt_direct_path=mock_attempt_direct_path,
144+
credentials=mock_creds,
145+
options=expected_options,
107146
)
108147
mock_transport_cls.assere_with(channel=channel_sentinel)
109148
mock_grpc_gapic_client.assert_called_once_with(
@@ -115,7 +154,6 @@ def test_grpc_client_property(self, mock_grpc_gapic_client):
115154

116155
@mock.patch("google.cloud._storage_v2.StorageAsyncClient")
117156
def test_grpc_client_with_anon_creds(self, mock_grpc_gapic_client):
118-
from google.cloud.storage._experimental.asyncio import async_grpc_client
119157

120158
# Arrange
121159
mock_transport_cls = mock.MagicMock()
@@ -133,7 +171,12 @@ def test_grpc_client_with_anon_creds(self, mock_grpc_gapic_client):
133171
# Assert
134172
self.assertIs(retrieved_client, mock_grpc_gapic_client.return_value)
135173

174+
primary_user_agent = DEFAULT_CLIENT_INFO.to_user_agent()
175+
expected_options = (("grpc.primary_user_agent", primary_user_agent),)
176+
136177
mock_transport_cls.create_channel.assert_called_once_with(
137-
attempt_direct_path=True, credentials=anonymous_creds
178+
attempt_direct_path=True,
179+
credentials=anonymous_creds,
180+
options=expected_options,
138181
)
139182
mock_transport_cls.assert_called_once_with(channel=channel_sentinel)

0 commit comments

Comments
 (0)