Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@

from .client import ConfigClient
from .transports.base import DEFAULT_CLIENT_INFO, ConfigTransport
from .transports.grpc import ConfigGrpcTransport
from .transports.grpc_asyncio import ConfigGrpcAsyncIOTransport
from .transports.rest import ConfigRestTransport

try:
from google.api_core import client_logging # type: ignore
Expand Down Expand Up @@ -284,11 +286,25 @@ def __init__(
google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
creation failed for any reason.
"""
transport_provided = isinstance(transport, ConfigTransport)

if (
transport_provided
and isinstance(transport, (ConfigGrpcTransport, ConfigRestTransport))
) or (
isinstance(transport, str) and transport in ["grpc", "rest"]
):
Comment on lines +289 to +296
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The transport_provided variable is redundant here. The condition can be simplified by removing this variable and its usage, as isinstance(transport, (ConfigGrpcTransport, ConfigRestTransport)) already handles cases where transport is not a transport object (like a string).

        if isinstance(transport, (ConfigGrpcTransport, ConfigRestTransport)) or (
            isinstance(transport, str) and transport in ["grpc", "rest"]
        ):

raise ValueError(
"The `ConfigAsyncClient` does not support sync transports. "
"Please use `ConfigClient` instead."
)

self._client = ConfigClient(
credentials=credentials,
transport=transport,
client_options=client_options,
client_info=client_info,
_allow_async_transport=True,
)

if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,7 @@ def __init__(
] = None,
client_options: Optional[Union[client_options_lib.ClientOptions, dict]] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
_allow_async_transport: bool = False,
) -> None:
"""Instantiates the config client.

Expand Down Expand Up @@ -870,6 +871,18 @@ def __init__(
# Ordinarily, we provide the transport, but allowing a custom transport
# instance provides an extensibility point for unusual situations.
transport_provided = isinstance(transport, ConfigTransport)

# Raise an exception if the async transport is provided.
if (
not _allow_async_transport
and transport_provided
and isinstance(transport, ConfigGrpcAsyncIOTransport)
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

ConfigGrpcAsyncIOTransport is used here but it is not imported in this file, which will cause a NameError at runtime. To resolve this, please add from .transports.grpc_asyncio import ConfigGrpcAsyncIOTransport at the top of the file.

):
raise ValueError(
"The `ConfigClient` does not support async transports. "
"Please use `ConfigAsyncClient` instead."
)

if transport_provided:
# transport is a ConfigTransport instance.
if credentials or self._client_options.credentials_file or api_key_value:
Expand Down Expand Up @@ -909,6 +922,18 @@ def __init__(
if isinstance(transport, str) or transport is None
else cast(Callable[..., ConfigTransport], transport)
)

# Raise an exception if the async transport is provided.
if (
not _allow_async_transport
and isinstance(transport, str)
and transport == "grpc_asyncio"
):
raise ValueError(
"The `ConfigClient` does not support async transports. "
"Please use `ConfigAsyncClient` instead."
)
Comment on lines +927 to +935
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This validation logic for async transports is duplicated. A similar check exists for transport objects earlier in the method (around line 876). To improve maintainability and avoid code duplication, consider consolidating both checks into a single location at the beginning of the __init__ method.


# initialize with the provided callable or the passed in class
self._transport = transport_init(
credentials=credentials,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import pytest
from unittest import mock
from google.cloud.config_v1 import ConfigClient, ConfigAsyncClient
from google.cloud.config_v1.services.config import transports
from google.auth import credentials as ga_credentials

def test_config_client_validates_grpc_asyncio_string():
creds = ga_credentials.AnonymousCredentials()
with pytest.raises(ValueError) as excinfo:
ConfigClient(credentials=creds, transport="grpc_asyncio")
assert "ConfigClient" in str(excinfo.value)
assert "async transports" in str(excinfo.value)

def test_config_client_validates_grpc_asyncio_object():
creds = ga_credentials.AnonymousCredentials()
transport = transports.ConfigGrpcAsyncIOTransport(credentials=creds)
with pytest.raises(ValueError) as excinfo:
ConfigClient(transport=transport)
assert "ConfigClient" in str(excinfo.value)
assert "async transports" in str(excinfo.value)

def test_config_client_allows_custom_transport():
creds = ga_credentials.AnonymousCredentials()
class CustomTransport(transports.ConfigTransport):
pass
transport = CustomTransport(credentials=creds)
# Should not raise
ConfigClient(transport=transport)

def test_config_async_client_validates_grpc_string():
creds = ga_credentials.AnonymousCredentials()
with pytest.raises(ValueError) as excinfo:
ConfigAsyncClient(credentials=creds, transport="grpc")
assert "ConfigAsyncClient" in str(excinfo.value)
assert "sync transports" in str(excinfo.value)

def test_config_async_client_validates_rest_string():
creds = ga_credentials.AnonymousCredentials()
with pytest.raises(ValueError) as excinfo:
ConfigAsyncClient(credentials=creds, transport="rest")
assert "ConfigAsyncClient" in str(excinfo.value)
assert "sync transports" in str(excinfo.value)

def test_config_async_client_validates_grpc_object():
creds = ga_credentials.AnonymousCredentials()
transport = transports.ConfigGrpcTransport(credentials=creds)
with pytest.raises(ValueError) as excinfo:
ConfigAsyncClient(transport=transport)
assert "ConfigAsyncClient" in str(excinfo.value)
assert "sync transports" in str(excinfo.value)

def test_config_async_client_allows_custom_transport():
creds = ga_credentials.AnonymousCredentials()
class CustomTransport(transports.ConfigTransport):
pass
transport = CustomTransport(credentials=creds)
# Should not raise
ConfigAsyncClient(transport=transport)

def test_config_async_client_initialization_happy_path():
creds = ga_credentials.AnonymousCredentials()
# Should not raise
client = ConfigAsyncClient(credentials=creds)
assert isinstance(client._client, ConfigClient)
Original file line number Diff line number Diff line change
Expand Up @@ -25421,7 +25421,6 @@ def test_config_http_transport_client_cert_source_for_mtls():
"transport_name",
[
"grpc",
"grpc_asyncio",
"rest",
],
)
Expand All @@ -25444,7 +25443,6 @@ def test_config_host_no_port(transport_name):
"transport_name",
[
"grpc",
"grpc_asyncio",
"rest",
],
)
Expand Down
Loading