From aedbee98cc6e56543c008ed5dc7f997d65e33944 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Tue, 30 Jun 2026 06:42:13 +0000 Subject: [PATCH 01/17] feat: apply PEP 0810 lazy imports to google-cloud-compute --- .../google/cloud/compute_v1/__init__.py | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py b/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py index f66c8a775ee4..77cfaeedf3c2 100644 --- a/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py +++ b/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py @@ -23,6 +23,135 @@ from importlib import metadata +if sys.version_info >= (3, 15): + __lazy_modules__ = { + f"{__name__}.services.accelerator_types", + f"{__name__}.services.addresses", + f"{__name__}.services.advice", + f"{__name__}.services.autoscalers", + f"{__name__}.services.backend_buckets", + f"{__name__}.services.backend_services", + f"{__name__}.services.cross_site_networks", + f"{__name__}.services.disk_types", + f"{__name__}.services.disks", + f"{__name__}.services.external_vpn_gateways", + f"{__name__}.services.firewall_policies", + f"{__name__}.services.firewalls", + f"{__name__}.services.forwarding_rules", + f"{__name__}.services.future_reservations", + f"{__name__}.services.global_addresses", + f"{__name__}.services.global_forwarding_rules", + f"{__name__}.services.global_network_endpoint_groups", + f"{__name__}.services.global_operations", + f"{__name__}.services.global_organization_operations", + f"{__name__}.services.global_public_delegated_prefixes", + f"{__name__}.services.global_vm_extension_policies", + f"{__name__}.services.health_checks", + f"{__name__}.services.image_family_views", + f"{__name__}.services.images", + f"{__name__}.services.instance_group_manager_resize_requests", + f"{__name__}.services.instance_group_managers", + f"{__name__}.services.instance_groups", + f"{__name__}.services.instance_settings_service", + f"{__name__}.services.instance_templates", + f"{__name__}.services.instances", + f"{__name__}.services.instant_snapshot_groups", + f"{__name__}.services.instant_snapshots", + f"{__name__}.services.interconnect_attachment_groups", + f"{__name__}.services.interconnect_attachments", + f"{__name__}.services.interconnect_groups", + f"{__name__}.services.interconnect_locations", + f"{__name__}.services.interconnect_remote_locations", + f"{__name__}.services.interconnects", + f"{__name__}.services.license_codes", + f"{__name__}.services.licenses", + f"{__name__}.services.machine_images", + f"{__name__}.services.machine_types", + f"{__name__}.services.network_attachments", + f"{__name__}.services.network_edge_security_services", + f"{__name__}.services.network_endpoint_groups", + f"{__name__}.services.network_firewall_policies", + f"{__name__}.services.network_profiles", + f"{__name__}.services.networks", + f"{__name__}.services.node_groups", + f"{__name__}.services.node_templates", + f"{__name__}.services.node_types", + f"{__name__}.services.organization_security_policies", + f"{__name__}.services.packet_mirrorings", + f"{__name__}.services.preview_features", + f"{__name__}.services.projects", + f"{__name__}.services.public_advertised_prefixes", + f"{__name__}.services.public_delegated_prefixes", + f"{__name__}.services.region_autoscalers", + f"{__name__}.services.region_backend_buckets", + f"{__name__}.services.region_backend_services", + f"{__name__}.services.region_commitments", + f"{__name__}.services.region_composite_health_checks", + f"{__name__}.services.region_disk_types", + f"{__name__}.services.region_disks", + f"{__name__}.services.region_health_aggregation_policies", + f"{__name__}.services.region_health_check_services", + f"{__name__}.services.region_health_checks", + f"{__name__}.services.region_health_sources", + f"{__name__}.services.region_instance_group_manager_resize_requests", + f"{__name__}.services.region_instance_group_managers", + f"{__name__}.services.region_instance_groups", + f"{__name__}.services.region_instance_templates", + f"{__name__}.services.region_instances", + f"{__name__}.services.region_instant_snapshot_groups", + f"{__name__}.services.region_instant_snapshots", + f"{__name__}.services.region_network_endpoint_groups", + f"{__name__}.services.region_network_firewall_policies", + f"{__name__}.services.region_notification_endpoints", + f"{__name__}.services.region_operations", + f"{__name__}.services.region_security_policies", + f"{__name__}.services.region_snapshot_settings", + f"{__name__}.services.region_snapshots", + f"{__name__}.services.region_ssl_certificates", + f"{__name__}.services.region_ssl_policies", + f"{__name__}.services.region_target_http_proxies", + f"{__name__}.services.region_target_https_proxies", + f"{__name__}.services.region_target_tcp_proxies", + f"{__name__}.services.region_url_maps", + f"{__name__}.services.region_zones", + f"{__name__}.services.regions", + f"{__name__}.services.reservation_blocks", + f"{__name__}.services.reservation_slots", + f"{__name__}.services.reservation_sub_blocks", + f"{__name__}.services.reservations", + f"{__name__}.services.resource_policies", + f"{__name__}.services.rollout_plans", + f"{__name__}.services.rollouts", + f"{__name__}.services.routers", + f"{__name__}.services.routes", + f"{__name__}.services.security_policies", + f"{__name__}.services.service_attachments", + f"{__name__}.services.snapshot_settings_service", + f"{__name__}.services.snapshots", + f"{__name__}.services.ssl_certificates", + f"{__name__}.services.ssl_policies", + f"{__name__}.services.storage_pool_types", + f"{__name__}.services.storage_pools", + f"{__name__}.services.subnetworks", + f"{__name__}.services.target_grpc_proxies", + f"{__name__}.services.target_http_proxies", + f"{__name__}.services.target_https_proxies", + f"{__name__}.services.target_instances", + f"{__name__}.services.target_pools", + f"{__name__}.services.target_ssl_proxies", + f"{__name__}.services.target_tcp_proxies", + f"{__name__}.services.target_vpn_gateways", + f"{__name__}.services.url_maps", + f"{__name__}.services.vpn_gateways", + f"{__name__}.services.vpn_tunnels", + f"{__name__}.services.wire_groups", + f"{__name__}.services.zone_operations", + f"{__name__}.services.zone_vm_extension_policies", + f"{__name__}.services.zones", + f"{__name__}.types.compute", + } + + from .services.accelerator_types import AcceleratorTypesClient from .services.addresses import AddressesClient from .services.advice import AdviceClient From 4b3ba9d94992660c889d6d8ce456be69dfc10457 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Tue, 30 Jun 2026 17:46:39 +0000 Subject: [PATCH 02/17] fix: remove python version guard for PEP 810 lazy imports --- .../google/cloud/compute_v1/__init__.py | 257 +++++++++--------- 1 file changed, 130 insertions(+), 127 deletions(-) diff --git a/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py b/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py index 77cfaeedf3c2..0e7172248bd9 100644 --- a/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py +++ b/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py @@ -23,133 +23,136 @@ from importlib import metadata -if sys.version_info >= (3, 15): - __lazy_modules__ = { - f"{__name__}.services.accelerator_types", - f"{__name__}.services.addresses", - f"{__name__}.services.advice", - f"{__name__}.services.autoscalers", - f"{__name__}.services.backend_buckets", - f"{__name__}.services.backend_services", - f"{__name__}.services.cross_site_networks", - f"{__name__}.services.disk_types", - f"{__name__}.services.disks", - f"{__name__}.services.external_vpn_gateways", - f"{__name__}.services.firewall_policies", - f"{__name__}.services.firewalls", - f"{__name__}.services.forwarding_rules", - f"{__name__}.services.future_reservations", - f"{__name__}.services.global_addresses", - f"{__name__}.services.global_forwarding_rules", - f"{__name__}.services.global_network_endpoint_groups", - f"{__name__}.services.global_operations", - f"{__name__}.services.global_organization_operations", - f"{__name__}.services.global_public_delegated_prefixes", - f"{__name__}.services.global_vm_extension_policies", - f"{__name__}.services.health_checks", - f"{__name__}.services.image_family_views", - f"{__name__}.services.images", - f"{__name__}.services.instance_group_manager_resize_requests", - f"{__name__}.services.instance_group_managers", - f"{__name__}.services.instance_groups", - f"{__name__}.services.instance_settings_service", - f"{__name__}.services.instance_templates", - f"{__name__}.services.instances", - f"{__name__}.services.instant_snapshot_groups", - f"{__name__}.services.instant_snapshots", - f"{__name__}.services.interconnect_attachment_groups", - f"{__name__}.services.interconnect_attachments", - f"{__name__}.services.interconnect_groups", - f"{__name__}.services.interconnect_locations", - f"{__name__}.services.interconnect_remote_locations", - f"{__name__}.services.interconnects", - f"{__name__}.services.license_codes", - f"{__name__}.services.licenses", - f"{__name__}.services.machine_images", - f"{__name__}.services.machine_types", - f"{__name__}.services.network_attachments", - f"{__name__}.services.network_edge_security_services", - f"{__name__}.services.network_endpoint_groups", - f"{__name__}.services.network_firewall_policies", - f"{__name__}.services.network_profiles", - f"{__name__}.services.networks", - f"{__name__}.services.node_groups", - f"{__name__}.services.node_templates", - f"{__name__}.services.node_types", - f"{__name__}.services.organization_security_policies", - f"{__name__}.services.packet_mirrorings", - f"{__name__}.services.preview_features", - f"{__name__}.services.projects", - f"{__name__}.services.public_advertised_prefixes", - f"{__name__}.services.public_delegated_prefixes", - f"{__name__}.services.region_autoscalers", - f"{__name__}.services.region_backend_buckets", - f"{__name__}.services.region_backend_services", - f"{__name__}.services.region_commitments", - f"{__name__}.services.region_composite_health_checks", - f"{__name__}.services.region_disk_types", - f"{__name__}.services.region_disks", - f"{__name__}.services.region_health_aggregation_policies", - f"{__name__}.services.region_health_check_services", - f"{__name__}.services.region_health_checks", - f"{__name__}.services.region_health_sources", - f"{__name__}.services.region_instance_group_manager_resize_requests", - f"{__name__}.services.region_instance_group_managers", - f"{__name__}.services.region_instance_groups", - f"{__name__}.services.region_instance_templates", - f"{__name__}.services.region_instances", - f"{__name__}.services.region_instant_snapshot_groups", - f"{__name__}.services.region_instant_snapshots", - f"{__name__}.services.region_network_endpoint_groups", - f"{__name__}.services.region_network_firewall_policies", - f"{__name__}.services.region_notification_endpoints", - f"{__name__}.services.region_operations", - f"{__name__}.services.region_security_policies", - f"{__name__}.services.region_snapshot_settings", - f"{__name__}.services.region_snapshots", - f"{__name__}.services.region_ssl_certificates", - f"{__name__}.services.region_ssl_policies", - f"{__name__}.services.region_target_http_proxies", - f"{__name__}.services.region_target_https_proxies", - f"{__name__}.services.region_target_tcp_proxies", - f"{__name__}.services.region_url_maps", - f"{__name__}.services.region_zones", - f"{__name__}.services.regions", - f"{__name__}.services.reservation_blocks", - f"{__name__}.services.reservation_slots", - f"{__name__}.services.reservation_sub_blocks", - f"{__name__}.services.reservations", - f"{__name__}.services.resource_policies", - f"{__name__}.services.rollout_plans", - f"{__name__}.services.rollouts", - f"{__name__}.services.routers", - f"{__name__}.services.routes", - f"{__name__}.services.security_policies", - f"{__name__}.services.service_attachments", - f"{__name__}.services.snapshot_settings_service", - f"{__name__}.services.snapshots", - f"{__name__}.services.ssl_certificates", - f"{__name__}.services.ssl_policies", - f"{__name__}.services.storage_pool_types", - f"{__name__}.services.storage_pools", - f"{__name__}.services.subnetworks", - f"{__name__}.services.target_grpc_proxies", - f"{__name__}.services.target_http_proxies", - f"{__name__}.services.target_https_proxies", - f"{__name__}.services.target_instances", - f"{__name__}.services.target_pools", - f"{__name__}.services.target_ssl_proxies", - f"{__name__}.services.target_tcp_proxies", - f"{__name__}.services.target_vpn_gateways", - f"{__name__}.services.url_maps", - f"{__name__}.services.vpn_gateways", - f"{__name__}.services.vpn_tunnels", - f"{__name__}.services.wire_groups", - f"{__name__}.services.zone_operations", - f"{__name__}.services.zone_vm_extension_policies", - f"{__name__}.services.zones", - f"{__name__}.types.compute", - } +# PEP 0810: Global Lazy Imports Control +# This variable provides a migration path for library maintainers. +# Python 3.15+ natively intercepts and defers these imports. +# Older Python versions safely ignore this variable. +__lazy_modules__ = { + f"{__name__}.services.accelerator_types", + f"{__name__}.services.addresses", + f"{__name__}.services.advice", + f"{__name__}.services.autoscalers", + f"{__name__}.services.backend_buckets", + f"{__name__}.services.backend_services", + f"{__name__}.services.cross_site_networks", + f"{__name__}.services.disk_types", + f"{__name__}.services.disks", + f"{__name__}.services.external_vpn_gateways", + f"{__name__}.services.firewall_policies", + f"{__name__}.services.firewalls", + f"{__name__}.services.forwarding_rules", + f"{__name__}.services.future_reservations", + f"{__name__}.services.global_addresses", + f"{__name__}.services.global_forwarding_rules", + f"{__name__}.services.global_network_endpoint_groups", + f"{__name__}.services.global_operations", + f"{__name__}.services.global_organization_operations", + f"{__name__}.services.global_public_delegated_prefixes", + f"{__name__}.services.global_vm_extension_policies", + f"{__name__}.services.health_checks", + f"{__name__}.services.image_family_views", + f"{__name__}.services.images", + f"{__name__}.services.instance_group_manager_resize_requests", + f"{__name__}.services.instance_group_managers", + f"{__name__}.services.instance_groups", + f"{__name__}.services.instance_settings_service", + f"{__name__}.services.instance_templates", + f"{__name__}.services.instances", + f"{__name__}.services.instant_snapshot_groups", + f"{__name__}.services.instant_snapshots", + f"{__name__}.services.interconnect_attachment_groups", + f"{__name__}.services.interconnect_attachments", + f"{__name__}.services.interconnect_groups", + f"{__name__}.services.interconnect_locations", + f"{__name__}.services.interconnect_remote_locations", + f"{__name__}.services.interconnects", + f"{__name__}.services.license_codes", + f"{__name__}.services.licenses", + f"{__name__}.services.machine_images", + f"{__name__}.services.machine_types", + f"{__name__}.services.network_attachments", + f"{__name__}.services.network_edge_security_services", + f"{__name__}.services.network_endpoint_groups", + f"{__name__}.services.network_firewall_policies", + f"{__name__}.services.network_profiles", + f"{__name__}.services.networks", + f"{__name__}.services.node_groups", + f"{__name__}.services.node_templates", + f"{__name__}.services.node_types", + f"{__name__}.services.organization_security_policies", + f"{__name__}.services.packet_mirrorings", + f"{__name__}.services.preview_features", + f"{__name__}.services.projects", + f"{__name__}.services.public_advertised_prefixes", + f"{__name__}.services.public_delegated_prefixes", + f"{__name__}.services.region_autoscalers", + f"{__name__}.services.region_backend_buckets", + f"{__name__}.services.region_backend_services", + f"{__name__}.services.region_commitments", + f"{__name__}.services.region_composite_health_checks", + f"{__name__}.services.region_disk_types", + f"{__name__}.services.region_disks", + f"{__name__}.services.region_health_aggregation_policies", + f"{__name__}.services.region_health_check_services", + f"{__name__}.services.region_health_checks", + f"{__name__}.services.region_health_sources", + f"{__name__}.services.region_instance_group_manager_resize_requests", + f"{__name__}.services.region_instance_group_managers", + f"{__name__}.services.region_instance_groups", + f"{__name__}.services.region_instance_templates", + f"{__name__}.services.region_instances", + f"{__name__}.services.region_instant_snapshot_groups", + f"{__name__}.services.region_instant_snapshots", + f"{__name__}.services.region_network_endpoint_groups", + f"{__name__}.services.region_network_firewall_policies", + f"{__name__}.services.region_notification_endpoints", + f"{__name__}.services.region_operations", + f"{__name__}.services.region_security_policies", + f"{__name__}.services.region_snapshot_settings", + f"{__name__}.services.region_snapshots", + f"{__name__}.services.region_ssl_certificates", + f"{__name__}.services.region_ssl_policies", + f"{__name__}.services.region_target_http_proxies", + f"{__name__}.services.region_target_https_proxies", + f"{__name__}.services.region_target_tcp_proxies", + f"{__name__}.services.region_url_maps", + f"{__name__}.services.region_zones", + f"{__name__}.services.regions", + f"{__name__}.services.reservation_blocks", + f"{__name__}.services.reservation_slots", + f"{__name__}.services.reservation_sub_blocks", + f"{__name__}.services.reservations", + f"{__name__}.services.resource_policies", + f"{__name__}.services.rollout_plans", + f"{__name__}.services.rollouts", + f"{__name__}.services.routers", + f"{__name__}.services.routes", + f"{__name__}.services.security_policies", + f"{__name__}.services.service_attachments", + f"{__name__}.services.snapshot_settings_service", + f"{__name__}.services.snapshots", + f"{__name__}.services.ssl_certificates", + f"{__name__}.services.ssl_policies", + f"{__name__}.services.storage_pool_types", + f"{__name__}.services.storage_pools", + f"{__name__}.services.subnetworks", + f"{__name__}.services.target_grpc_proxies", + f"{__name__}.services.target_http_proxies", + f"{__name__}.services.target_https_proxies", + f"{__name__}.services.target_instances", + f"{__name__}.services.target_pools", + f"{__name__}.services.target_ssl_proxies", + f"{__name__}.services.target_tcp_proxies", + f"{__name__}.services.target_vpn_gateways", + f"{__name__}.services.url_maps", + f"{__name__}.services.vpn_gateways", + f"{__name__}.services.vpn_tunnels", + f"{__name__}.services.wire_groups", + f"{__name__}.services.zone_operations", + f"{__name__}.services.zone_vm_extension_policies", + f"{__name__}.services.zones", + f"{__name__}.types.compute", +} from .services.accelerator_types import AcceleratorTypesClient From c60e977e8f42c9c5d30113451ab88699325c1ccd Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 06:25:56 +0000 Subject: [PATCH 03/17] feat: centralize service-agnostic boilerplate into api_core for GAPIC clients --- .../google/api_core/gapic_v1/__init__.py | 3 + .../api_core/gapic_v1/client_helpers.py | 128 ++++++++++++++++++ .../services/key_management_service/client.py | 69 +++------- 3 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 packages/google-api-core/google/api_core/gapic_v1/client_helpers.py diff --git a/packages/google-api-core/google/api_core/gapic_v1/__init__.py b/packages/google-api-core/google/api_core/gapic_v1/__init__.py index e5b7ad352ee6..992dacff883d 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/__init__.py +++ b/packages/google-api-core/google/api_core/gapic_v1/__init__.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from google.api_core.gapic_v1 import client_helpers from google.api_core.gapic_v1 import client_info from google.api_core.gapic_v1 import config from google.api_core.gapic_v1 import config_async @@ -20,6 +21,7 @@ from google.api_core.gapic_v1 import routing_header __all__ = [ + "client_helpers", "client_info", "config", "config_async", @@ -27,3 +29,4 @@ "method_async", "routing_header", ] + diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py new file mode 100644 index 000000000000..79d5afc28b1e --- /dev/null +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 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. +# + +"""Helpers for GAPIC client initialization.""" + +import os +import re +from typing import Optional + +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.auth.transport import mtls # type: ignore + + + +def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint. + + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + Optional[str]: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + if m is None: + # Could not parse api_endpoint; return as-is. + return api_endpoint + + name, mtls_group, sandbox, googledomain = m.groups() + if mtls_group or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + +def use_client_cert_effective() -> bool: + """Returns whether client certificate should be used for mTLS if the + google-auth version supports should_use_client_cert automatic mTLS enablement. + + Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var. + + Returns: + bool: whether client certificate should be used for mTLS + Raises: + ValueError: (If using a version of google-auth without should_use_client_cert and + GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) + """ + # check if google-auth version supports should_use_client_cert for automatic mTLS enablement + if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER + return mtls.should_use_client_cert() + else: # pragma: NO COVER + # if unsupported, fallback to reading from env var + use_client_cert_str = os.getenv( + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" + ).lower() + if use_client_cert_str not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" + " either `true` or `false`" + ) + return use_client_cert_str == "true" + + +def get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[bytes], + universe_domain: str, + use_mtls_endpoint: str, + default_universe: str, + default_mtls_endpoint: Optional[str], + default_endpoint_template: str, +) -> str: + """Return the API endpoint used by the client. + + Args: + api_override (Optional[str]): The API endpoint override. + client_cert_source (Optional[bytes]): The client certificate source. + universe_domain (str): The universe domain. + use_mtls_endpoint (str): How to use the mTLS endpoint. + default_universe (str): The default universe. + default_mtls_endpoint (Optional[str]): The default mTLS endpoint. + default_endpoint_template (str): The default endpoint template. + + Returns: + str: The API endpoint to be used by the client. + """ + if api_override is not None: + api_endpoint = api_override + elif use_mtls_endpoint == "always" or ( + use_mtls_endpoint == "auto" and client_cert_source + ): + if universe_domain != default_universe: + raise MutualTLSChannelError( + f"mTLS is not supported in any universe other than {default_universe}." + ) + api_endpoint = default_mtls_endpoint + else: + api_endpoint = default_endpoint_template.format( + UNIVERSE_DOMAIN=universe_domain + ) + return api_endpoint + diff --git a/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py b/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py index 61224eacb11a..0309806577b0 100644 --- a/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py +++ b/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py @@ -145,28 +145,7 @@ def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: Returns: Optional[str]: converted mTLS api endpoint. """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + return gapic_v1.client_helpers.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "cloudkms.googleapis.com" @@ -190,20 +169,10 @@ def _use_client_cert_effective(): ValueError: (If using a version of google-auth without should_use_client_cert and GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) """ - # check if google-auth version supports should_use_client_cert for automatic mTLS enablement - if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER - return mtls.should_use_client_cert() - else: # pragma: NO COVER - # if unsupported, fallback to reading from env var - use_client_cert_str = os.getenv( - "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" - ).lower() - if use_client_cert_str not in ("true", "false"): - raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" - " either `true` or `false`" - ) - return use_client_cert_str == "true" + return gapic_v1.client_helpers.use_client_cert_effective() + + + @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): @@ -600,22 +569,18 @@ def _get_api_endpoint( Returns: str: The API endpoint to be used by the client. """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or ( - use_mtls_endpoint == "auto" and client_cert_source - ): - _default_universe = KeyManagementServiceClient._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError( - f"mTLS is not supported in any universe other than {_default_universe}." - ) - api_endpoint = KeyManagementServiceClient.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = KeyManagementServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format( - UNIVERSE_DOMAIN=universe_domain - ) - return api_endpoint + return gapic_v1.client_helpers.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + KeyManagementServiceClient._DEFAULT_UNIVERSE, + KeyManagementServiceClient.DEFAULT_MTLS_ENDPOINT, + KeyManagementServiceClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + + + @staticmethod def _get_universe_domain( From 4463094f7fabed0471ee2418ba88ee3801d73594 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh <137334116+hebaalazzeh@users.noreply.github.com> Date: Sun, 5 Jul 2026 23:33:00 -0700 Subject: [PATCH 04/17] Update packages/google-api-core/google/api_core/gapic_v1/client_helpers.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../google-api-core/google/api_core/gapic_v1/client_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index 79d5afc28b1e..68a3d3791717 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -18,7 +18,7 @@ import os import re -from typing import Optional +from typing import Any, Optional from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.auth.transport import mtls # type: ignore From 4d9bea23809f0f7340ae16002a0ad06a76d55bf9 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 06:38:09 +0000 Subject: [PATCH 05/17] refactor: optimize regex and update type hints in client_helpers --- .../api_core/gapic_v1/client_helpers.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index 68a3d3791717..5d983e8a9ff1 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -23,6 +23,11 @@ from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.auth.transport import mtls # type: ignore +_MTLS_ENDPOINT_RE = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" +) + + def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: @@ -38,11 +43,7 @@ def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: if not api_endpoint: return api_endpoint - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) + m = _MTLS_ENDPOINT_RE.match(api_endpoint) if m is None: # Could not parse api_endpoint; return as-is. return api_endpoint @@ -89,18 +90,18 @@ def use_client_cert_effective() -> bool: def get_api_endpoint( api_override: Optional[str], - client_cert_source: Optional[bytes], + client_cert_source: Optional[Any], universe_domain: str, use_mtls_endpoint: str, default_universe: str, default_mtls_endpoint: Optional[str], default_endpoint_template: str, -) -> str: +) -> Optional[str]: """Return the API endpoint used by the client. Args: api_override (Optional[str]): The API endpoint override. - client_cert_source (Optional[bytes]): The client certificate source. + client_cert_source (Optional[Any]): The client certificate source. universe_domain (str): The universe domain. use_mtls_endpoint (str): How to use the mTLS endpoint. default_universe (str): The default universe. @@ -108,7 +109,7 @@ def get_api_endpoint( default_endpoint_template (str): The default endpoint template. Returns: - str: The API endpoint to be used by the client. + Optional[str]: The API endpoint to be used by the client. """ if api_override is not None: api_endpoint = api_override From 54cf6a043be4cff8e4033c90847e02211fd22d95 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 06:46:53 +0000 Subject: [PATCH 06/17] style: run black formatter on client_helpers.py --- .../google/api_core/gapic_v1/client_helpers.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index 5d983e8a9ff1..4790f2ca0f6e 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -28,8 +28,6 @@ ) - - def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint. @@ -122,8 +120,5 @@ def get_api_endpoint( ) api_endpoint = default_mtls_endpoint else: - api_endpoint = default_endpoint_template.format( - UNIVERSE_DOMAIN=universe_domain - ) + api_endpoint = default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) return api_endpoint - From 69260e514532934e73e86d1e75fcf345560243a1 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 06:53:11 +0000 Subject: [PATCH 07/17] fix: address lint and mypy issues in api_core and kms --- .../google-api-core/google/api_core/gapic_v1/__init__.py | 1 - .../google/api_core/gapic_v1/client_helpers.py | 8 ++++---- .../kms_v1/services/key_management_service/client.py | 6 ------ 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/__init__.py b/packages/google-api-core/google/api_core/gapic_v1/__init__.py index 992dacff883d..e8cae980d58a 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/__init__.py +++ b/packages/google-api-core/google/api_core/gapic_v1/__init__.py @@ -29,4 +29,3 @@ "method_async", "routing_header", ] - diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index 4790f2ca0f6e..d34a8f1ad819 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -110,7 +110,7 @@ def get_api_endpoint( Optional[str]: The API endpoint to be used by the client. """ if api_override is not None: - api_endpoint = api_override + return api_override elif use_mtls_endpoint == "always" or ( use_mtls_endpoint == "auto" and client_cert_source ): @@ -118,7 +118,7 @@ def get_api_endpoint( raise MutualTLSChannelError( f"mTLS is not supported in any universe other than {default_universe}." ) - api_endpoint = default_mtls_endpoint + return default_mtls_endpoint else: - api_endpoint = default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + return default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) + diff --git a/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py b/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py index 0309806577b0..15ada5dff26d 100644 --- a/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py +++ b/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py @@ -171,9 +171,6 @@ def _use_client_cert_effective(): """ return gapic_v1.client_helpers.use_client_cert_effective() - - - @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -579,9 +576,6 @@ def _get_api_endpoint( KeyManagementServiceClient._DEFAULT_ENDPOINT_TEMPLATE, ) - - - @staticmethod def _get_universe_domain( client_universe_domain: Optional[str], universe_domain_env: Optional[str] From 46e07972677a7175c4bb3fd2e8dea3ceb149e374 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 07:19:49 +0000 Subject: [PATCH 08/17] fix: address black formatting issue in client_helpers.py --- .../google-api-core/google/api_core/gapic_v1/client_helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index d34a8f1ad819..3a50885150ae 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -121,4 +121,3 @@ def get_api_endpoint( return default_mtls_endpoint else: return default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) - From a4513c44bc5eac473831119267eea5ce579f3163 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 15:52:15 +0000 Subject: [PATCH 09/17] fix: add underscore prefix to internal helpers in client_helpers.py --- .../google/api_core/gapic_v1/client_helpers.py | 6 +++--- .../cloud/kms_v1/services/key_management_service/client.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index 3a50885150ae..3e0756364662 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -28,7 +28,7 @@ ) -def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: +def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint. Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to @@ -58,7 +58,7 @@ def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") -def use_client_cert_effective() -> bool: +def _use_client_cert_effective() -> bool: """Returns whether client certificate should be used for mTLS if the google-auth version supports should_use_client_cert automatic mTLS enablement. @@ -86,7 +86,7 @@ def use_client_cert_effective() -> bool: return use_client_cert_str == "true" -def get_api_endpoint( +def _get_api_endpoint( api_override: Optional[str], client_cert_source: Optional[Any], universe_domain: str, diff --git a/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py b/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py index 15ada5dff26d..e50e52d21355 100644 --- a/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py +++ b/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py @@ -145,7 +145,7 @@ def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: Returns: Optional[str]: converted mTLS api endpoint. """ - return gapic_v1.client_helpers.get_default_mtls_endpoint(api_endpoint) + return gapic_v1.client_helpers._get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "cloudkms.googleapis.com" @@ -169,7 +169,7 @@ def _use_client_cert_effective(): ValueError: (If using a version of google-auth without should_use_client_cert and GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) """ - return gapic_v1.client_helpers.use_client_cert_effective() + return gapic_v1.client_helpers._use_client_cert_effective() @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): @@ -566,7 +566,7 @@ def _get_api_endpoint( Returns: str: The API endpoint to be used by the client. """ - return gapic_v1.client_helpers.get_api_endpoint( + return gapic_v1.client_helpers._get_api_endpoint( api_override, client_cert_source, universe_domain, From ad1c01fc5410cd8bc751d4b61b9f6e4e2f5b5aa2 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 21:21:16 +0000 Subject: [PATCH 10/17] test: add unit tests for gapic client_helpers --- .../tests/unit/gapic/test_client_helpers.py | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 packages/google-api-core/tests/unit/gapic/test_client_helpers.py diff --git a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py new file mode 100644 index 000000000000..ad5d49a17649 --- /dev/null +++ b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 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 os +from unittest import mock +import pytest + +from google.api_core.gapic_v1 import client_helpers +from google.auth.exceptions import MutualTLSChannelError + + +def test__get_default_mtls_endpoint(): + # Test valid API endpoints + assert client_helpers._get_default_mtls_endpoint("foo.googleapis.com") == "foo.mtls.googleapis.com" + assert client_helpers._get_default_mtls_endpoint("foo.sandbox.googleapis.com") == "foo.mtls.sandbox.googleapis.com" + + # Test endpoints that shouldn't be converted + assert client_helpers._get_default_mtls_endpoint("foo.mtls.googleapis.com") == "foo.mtls.googleapis.com" + assert client_helpers._get_default_mtls_endpoint("foo.com") == "foo.com" + + # Test empty/None endpoints + assert client_helpers._get_default_mtls_endpoint("") == "" + assert client_helpers._get_default_mtls_endpoint(None) is None + + +@mock.patch("google.auth.transport.mtls.should_use_client_cert", autospec=True) +def test__use_client_cert_effective_with_google_auth(mock_should_use_client_cert): + # Test when google-auth supports the method + mock_should_use_client_cert.return_value = True + assert client_helpers._use_client_cert_effective() is True + + mock_should_use_client_cert.return_value = False + assert client_helpers._use_client_cert_effective() is False + + +@mock.patch.dict(os.environ, {}, clear=True) +def test__use_client_cert_effective_fallback(): + # We must patch hasattr to simulate google-auth lacking the method + with mock.patch("google.api_core.gapic_v1.client_helpers.hasattr", return_value=False): + # Default is false + assert client_helpers._use_client_cert_effective() is False + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + assert client_helpers._use_client_cert_effective() is True + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}): + assert client_helpers._use_client_cert_effective() is False + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "invalid"}): + with pytest.raises(ValueError, match="must be either `true` or `false`"): + client_helpers._use_client_cert_effective() + + +def test__get_api_endpoint_override(): + # If api_override is provided, it should be returned regardless of other args + endpoint = client_helpers._get_api_endpoint( + api_override="custom.endpoint.com", + client_cert_source=None, + universe_domain="googleapis.com", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "custom.endpoint.com" + + +def test__get_api_endpoint_mtls_always(): + # use_mtls_endpoint == "always" should use the default mtls endpoint + endpoint = client_helpers._get_api_endpoint( + api_override=None, + client_cert_source=None, + universe_domain="googleapis.com", + use_mtls_endpoint="always", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "foo.mtls.googleapis.com" + + +def test__get_api_endpoint_mtls_auto_with_cert(): + # "auto" with client_cert_source should use mtls + endpoint = client_helpers._get_api_endpoint( + api_override=None, + client_cert_source=mock.Mock(), + universe_domain="googleapis.com", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "foo.mtls.googleapis.com" + + +def test__get_api_endpoint_mtls_auto_no_cert(): + # "auto" without client_cert_source should use the default template + endpoint = client_helpers._get_api_endpoint( + api_override=None, + client_cert_source=None, + universe_domain="googleapis.com", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "foo.googleapis.com" + + +def test__get_api_endpoint_mtls_universe_mismatch(): + # mTLS is only supported in the default universe + with pytest.raises(MutualTLSChannelError, match="mTLS is not supported"): + client_helpers._get_api_endpoint( + api_override=None, + client_cert_source=mock.Mock(), + universe_domain="custom-universe.com", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) From 1456ead1dec1011b42f948c992e89aa38a6b0b0f Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 21:49:28 +0000 Subject: [PATCH 11/17] chore: revert experimental kms validation changes --- .../services/key_management_service/client.py | 63 +++++++++++++++---- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py b/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py index e50e52d21355..61224eacb11a 100644 --- a/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py +++ b/packages/google-cloud-kms/google/cloud/kms_v1/services/key_management_service/client.py @@ -145,7 +145,28 @@ def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: Returns: Optional[str]: converted mTLS api endpoint. """ - return gapic_v1.client_helpers._get_default_mtls_endpoint(api_endpoint) + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + if m is None: + # Could not parse api_endpoint; return as-is. + return api_endpoint + + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "cloudkms.googleapis.com" @@ -169,7 +190,20 @@ def _use_client_cert_effective(): ValueError: (If using a version of google-auth without should_use_client_cert and GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) """ - return gapic_v1.client_helpers._use_client_cert_effective() + # check if google-auth version supports should_use_client_cert for automatic mTLS enablement + if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER + return mtls.should_use_client_cert() + else: # pragma: NO COVER + # if unsupported, fallback to reading from env var + use_client_cert_str = os.getenv( + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" + ).lower() + if use_client_cert_str not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" + " either `true` or `false`" + ) + return use_client_cert_str == "true" @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): @@ -566,15 +600,22 @@ def _get_api_endpoint( Returns: str: The API endpoint to be used by the client. """ - return gapic_v1.client_helpers._get_api_endpoint( - api_override, - client_cert_source, - universe_domain, - use_mtls_endpoint, - KeyManagementServiceClient._DEFAULT_UNIVERSE, - KeyManagementServiceClient.DEFAULT_MTLS_ENDPOINT, - KeyManagementServiceClient._DEFAULT_ENDPOINT_TEMPLATE, - ) + if api_override is not None: + api_endpoint = api_override + elif use_mtls_endpoint == "always" or ( + use_mtls_endpoint == "auto" and client_cert_source + ): + _default_universe = KeyManagementServiceClient._DEFAULT_UNIVERSE + if universe_domain != _default_universe: + raise MutualTLSChannelError( + f"mTLS is not supported in any universe other than {_default_universe}." + ) + api_endpoint = KeyManagementServiceClient.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = KeyManagementServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=universe_domain + ) + return api_endpoint @staticmethod def _get_universe_domain( From 0ffcdd39d2533fca64f221e9fe95687b120e6723 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 21:57:48 +0000 Subject: [PATCH 12/17] fix: resolve flake8 line length issues in tests --- .../tests/unit/gapic/test_client_helpers.py | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py index ad5d49a17649..39a37ded1ed1 100644 --- a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py +++ b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py @@ -23,48 +23,66 @@ def test__get_default_mtls_endpoint(): # Test valid API endpoints - assert client_helpers._get_default_mtls_endpoint("foo.googleapis.com") == "foo.mtls.googleapis.com" - assert client_helpers._get_default_mtls_endpoint("foo.sandbox.googleapis.com") == "foo.mtls.sandbox.googleapis.com" - + assert ( + client_helpers._get_default_mtls_endpoint("foo.googleapis.com") + == "foo.mtls.googleapis.com" + ) + assert ( + client_helpers._get_default_mtls_endpoint("foo.sandbox.googleapis.com") + == "foo.mtls.sandbox.googleapis.com" + ) + # Test endpoints that shouldn't be converted - assert client_helpers._get_default_mtls_endpoint("foo.mtls.googleapis.com") == "foo.mtls.googleapis.com" + assert ( + client_helpers._get_default_mtls_endpoint("foo.mtls.googleapis.com") + == "foo.mtls.googleapis.com" + ) assert client_helpers._get_default_mtls_endpoint("foo.com") == "foo.com" - + # Test empty/None endpoints assert client_helpers._get_default_mtls_endpoint("") == "" assert client_helpers._get_default_mtls_endpoint(None) is None @mock.patch("google.auth.transport.mtls.should_use_client_cert", autospec=True) -def test__use_client_cert_effective_with_google_auth(mock_should_use_client_cert): +def test__use_client_cert_effective_with_google_auth(mock_method): # Test when google-auth supports the method - mock_should_use_client_cert.return_value = True + mock_method.return_value = True assert client_helpers._use_client_cert_effective() is True - - mock_should_use_client_cert.return_value = False + + mock_method.return_value = False assert client_helpers._use_client_cert_effective() is False @mock.patch.dict(os.environ, {}, clear=True) def test__use_client_cert_effective_fallback(): # We must patch hasattr to simulate google-auth lacking the method - with mock.patch("google.api_core.gapic_v1.client_helpers.hasattr", return_value=False): + with mock.patch( + "google.api_core.gapic_v1.client_helpers.hasattr", return_value=False + ): # Default is false assert client_helpers._use_client_cert_effective() is False - - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + + env_true = {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"} + with mock.patch.dict(os.environ, env_true): assert client_helpers._use_client_cert_effective() is True - - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}): + + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"} + ): assert client_helpers._use_client_cert_effective() is False - - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "invalid"}): - with pytest.raises(ValueError, match="must be either `true` or `false`"): + + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "invalid"} + ): + match_str = "must be either `true` or `false`" + with pytest.raises(ValueError, match=match_str): client_helpers._use_client_cert_effective() def test__get_api_endpoint_override(): - # If api_override is provided, it should be returned regardless of other args + # If api_override is provided, it should be returned + # regardless of other args endpoint = client_helpers._get_api_endpoint( api_override="custom.endpoint.com", client_cert_source=None, From 2168bc012bc921cb9833a76bbce3be39984175a8 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 22:05:45 +0000 Subject: [PATCH 13/17] chore: revert experimental PEP 0810 lazy loading in compute --- .../google/cloud/compute_v1/__init__.py | 132 ------------------ 1 file changed, 132 deletions(-) diff --git a/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py b/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py index 0e7172248bd9..f66c8a775ee4 100644 --- a/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py +++ b/packages/google-cloud-compute/google/cloud/compute_v1/__init__.py @@ -23,138 +23,6 @@ from importlib import metadata -# PEP 0810: Global Lazy Imports Control -# This variable provides a migration path for library maintainers. -# Python 3.15+ natively intercepts and defers these imports. -# Older Python versions safely ignore this variable. -__lazy_modules__ = { - f"{__name__}.services.accelerator_types", - f"{__name__}.services.addresses", - f"{__name__}.services.advice", - f"{__name__}.services.autoscalers", - f"{__name__}.services.backend_buckets", - f"{__name__}.services.backend_services", - f"{__name__}.services.cross_site_networks", - f"{__name__}.services.disk_types", - f"{__name__}.services.disks", - f"{__name__}.services.external_vpn_gateways", - f"{__name__}.services.firewall_policies", - f"{__name__}.services.firewalls", - f"{__name__}.services.forwarding_rules", - f"{__name__}.services.future_reservations", - f"{__name__}.services.global_addresses", - f"{__name__}.services.global_forwarding_rules", - f"{__name__}.services.global_network_endpoint_groups", - f"{__name__}.services.global_operations", - f"{__name__}.services.global_organization_operations", - f"{__name__}.services.global_public_delegated_prefixes", - f"{__name__}.services.global_vm_extension_policies", - f"{__name__}.services.health_checks", - f"{__name__}.services.image_family_views", - f"{__name__}.services.images", - f"{__name__}.services.instance_group_manager_resize_requests", - f"{__name__}.services.instance_group_managers", - f"{__name__}.services.instance_groups", - f"{__name__}.services.instance_settings_service", - f"{__name__}.services.instance_templates", - f"{__name__}.services.instances", - f"{__name__}.services.instant_snapshot_groups", - f"{__name__}.services.instant_snapshots", - f"{__name__}.services.interconnect_attachment_groups", - f"{__name__}.services.interconnect_attachments", - f"{__name__}.services.interconnect_groups", - f"{__name__}.services.interconnect_locations", - f"{__name__}.services.interconnect_remote_locations", - f"{__name__}.services.interconnects", - f"{__name__}.services.license_codes", - f"{__name__}.services.licenses", - f"{__name__}.services.machine_images", - f"{__name__}.services.machine_types", - f"{__name__}.services.network_attachments", - f"{__name__}.services.network_edge_security_services", - f"{__name__}.services.network_endpoint_groups", - f"{__name__}.services.network_firewall_policies", - f"{__name__}.services.network_profiles", - f"{__name__}.services.networks", - f"{__name__}.services.node_groups", - f"{__name__}.services.node_templates", - f"{__name__}.services.node_types", - f"{__name__}.services.organization_security_policies", - f"{__name__}.services.packet_mirrorings", - f"{__name__}.services.preview_features", - f"{__name__}.services.projects", - f"{__name__}.services.public_advertised_prefixes", - f"{__name__}.services.public_delegated_prefixes", - f"{__name__}.services.region_autoscalers", - f"{__name__}.services.region_backend_buckets", - f"{__name__}.services.region_backend_services", - f"{__name__}.services.region_commitments", - f"{__name__}.services.region_composite_health_checks", - f"{__name__}.services.region_disk_types", - f"{__name__}.services.region_disks", - f"{__name__}.services.region_health_aggregation_policies", - f"{__name__}.services.region_health_check_services", - f"{__name__}.services.region_health_checks", - f"{__name__}.services.region_health_sources", - f"{__name__}.services.region_instance_group_manager_resize_requests", - f"{__name__}.services.region_instance_group_managers", - f"{__name__}.services.region_instance_groups", - f"{__name__}.services.region_instance_templates", - f"{__name__}.services.region_instances", - f"{__name__}.services.region_instant_snapshot_groups", - f"{__name__}.services.region_instant_snapshots", - f"{__name__}.services.region_network_endpoint_groups", - f"{__name__}.services.region_network_firewall_policies", - f"{__name__}.services.region_notification_endpoints", - f"{__name__}.services.region_operations", - f"{__name__}.services.region_security_policies", - f"{__name__}.services.region_snapshot_settings", - f"{__name__}.services.region_snapshots", - f"{__name__}.services.region_ssl_certificates", - f"{__name__}.services.region_ssl_policies", - f"{__name__}.services.region_target_http_proxies", - f"{__name__}.services.region_target_https_proxies", - f"{__name__}.services.region_target_tcp_proxies", - f"{__name__}.services.region_url_maps", - f"{__name__}.services.region_zones", - f"{__name__}.services.regions", - f"{__name__}.services.reservation_blocks", - f"{__name__}.services.reservation_slots", - f"{__name__}.services.reservation_sub_blocks", - f"{__name__}.services.reservations", - f"{__name__}.services.resource_policies", - f"{__name__}.services.rollout_plans", - f"{__name__}.services.rollouts", - f"{__name__}.services.routers", - f"{__name__}.services.routes", - f"{__name__}.services.security_policies", - f"{__name__}.services.service_attachments", - f"{__name__}.services.snapshot_settings_service", - f"{__name__}.services.snapshots", - f"{__name__}.services.ssl_certificates", - f"{__name__}.services.ssl_policies", - f"{__name__}.services.storage_pool_types", - f"{__name__}.services.storage_pools", - f"{__name__}.services.subnetworks", - f"{__name__}.services.target_grpc_proxies", - f"{__name__}.services.target_http_proxies", - f"{__name__}.services.target_https_proxies", - f"{__name__}.services.target_instances", - f"{__name__}.services.target_pools", - f"{__name__}.services.target_ssl_proxies", - f"{__name__}.services.target_tcp_proxies", - f"{__name__}.services.target_vpn_gateways", - f"{__name__}.services.url_maps", - f"{__name__}.services.vpn_gateways", - f"{__name__}.services.vpn_tunnels", - f"{__name__}.services.wire_groups", - f"{__name__}.services.zone_operations", - f"{__name__}.services.zone_vm_extension_policies", - f"{__name__}.services.zones", - f"{__name__}.types.compute", -} - - from .services.accelerator_types import AcceleratorTypesClient from .services.addresses import AddressesClient from .services.advice import AdviceClient From 5abeb855cda83a30f72bafea6a9d35ac62589375 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 22:25:21 +0000 Subject: [PATCH 14/17] feat: centralize additional client helpers to api_core --- .../api_core/gapic_v1/client_helpers.py | 144 ++++++++++++++++-- .../tests/unit/gapic/test_client_helpers.py | 109 +++++++++++++ 2 files changed, 243 insertions(+), 10 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index 3e0756364662..82aad16b1580 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -18,13 +18,15 @@ import os import re -from typing import Any, Optional +import uuid +from typing import Any, Optional, Tuple from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.auth.transport import mtls # type: ignore _MTLS_ENDPOINT_RE = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?" + r"(?P\.googleapis\.com)?" ) @@ -60,17 +62,20 @@ def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: def _use_client_cert_effective() -> bool: """Returns whether client certificate should be used for mTLS if the - google-auth version supports should_use_client_cert automatic mTLS enablement. + google-auth version supports should_use_client_cert automatic mTLS + enablement. Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var. Returns: bool: whether client certificate should be used for mTLS Raises: - ValueError: (If using a version of google-auth without should_use_client_cert and - GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) + ValueError: (If using a version of google-auth without + should_use_client_cert and GOOGLE_API_USE_CLIENT_CERTIFICATE is + set to an unexpected value.) """ - # check if google-auth version supports should_use_client_cert for automatic mTLS enablement + # check if google-auth version supports should_use_client_cert for + # automatic mTLS enablement if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER return mtls.should_use_client_cert() else: # pragma: NO COVER @@ -80,8 +85,8 @@ def _use_client_cert_effective() -> bool: ).lower() if use_client_cert_str not in ("true", "false"): raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" - " either `true` or `false`" + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` " + "must be either `true` or `false`" ) return use_client_cert_str == "true" @@ -116,8 +121,127 @@ def _get_api_endpoint( ): if universe_domain != default_universe: raise MutualTLSChannelError( - f"mTLS is not supported in any universe other than {default_universe}." + f"mTLS is not supported in any universe other than " + f"{default_universe}." ) return default_mtls_endpoint else: - return default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) + return default_endpoint_template.format( + UNIVERSE_DOMAIN=universe_domain + ) + + +def _read_environment_variables() -> Tuple[bool, str, Optional[str]]: + """Returns the environment variables used by the client. + + Returns: + Tuple[bool, str, Optional[str]]: returns the + GOOGLE_API_USE_CLIENT_CERTIFICATE, GOOGLE_API_USE_MTLS_ENDPOINT, + and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables. + + Raises: + ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not + any of ["true", "false"]. + google.auth.exceptions.MutualTLSChannelError: If + GOOGLE_API_USE_MTLS_ENDPOINT is not any of + ["auto", "never", "always"]. + """ + use_client_cert = _use_client_cert_effective() + use_mtls_endpoint = os.getenv( + "GOOGLE_API_USE_MTLS_ENDPOINT", "auto" + ).lower() + universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") + if use_mtls_endpoint not in ("auto", "never", "always"): + raise MutualTLSChannelError( + "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` " + "must be `never`, `auto` or `always`" + ) + return use_client_cert, use_mtls_endpoint, universe_domain_env + + +def _get_client_cert_source( + provided_cert_source: Optional[Any], use_cert_flag: bool +) -> Optional[Any]: + """Return the client cert source to be used by the client. + + Args: + provided_cert_source (bytes): The client certificate source provided. + use_cert_flag (bool): A flag indicating whether to use the + client certificate. + + Returns: + bytes or None: The client cert source to be used by the client. + """ + client_cert_source = None + if use_cert_flag: + if provided_cert_source: + client_cert_source = provided_cert_source + elif ( + hasattr(mtls, "has_default_client_cert_source") + and mtls.has_default_client_cert_source() + ): + client_cert_source = mtls.default_client_cert_source() + return client_cert_source + + +def _get_universe_domain( + client_universe_domain: Optional[str], + universe_domain_env: Optional[str], + default_universe: str, +) -> str: + """Return the universe domain used by the client. + + Args: + client_universe_domain (Optional[str]): The universe domain + configured via the client options. + universe_domain_env (Optional[str]): The universe domain + configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" env var. + default_universe (str): The default universe domain. + + Returns: + str: The universe domain to be used by the client. + + Raises: + ValueError: If the universe domain is an empty string. + """ + universe_domain = default_universe + if client_universe_domain is not None: + universe_domain = client_universe_domain + elif universe_domain_env is not None: + universe_domain = universe_domain_env + if len(universe_domain.strip()) == 0: + raise ValueError("Universe Domain cannot be an empty string.") + return universe_domain + + +def _setup_request_id( + request: Any, field_name: str, is_proto3_optional: bool +) -> None: + """Populate a UUID4 field in the request if it is not already set. + + Args: + request (Union[google.protobuf.message.Message, dict]): The + request object. + field_name (str): The name of the field to populate. + is_proto3_optional (bool): Whether the field is proto3 optional. + """ + if isinstance(request, dict): + if is_proto3_optional: + if field_name not in request: + request[field_name] = str(uuid.uuid4()) + elif not request.get(field_name): + request[field_name] = str(uuid.uuid4()) + return + + if is_proto3_optional: + try: + # Pure protobuf messages + if not request.HasField(field_name): + setattr(request, field_name, str(uuid.uuid4())) + except (AttributeError, ValueError): + # Proto-plus messages or other objects + if field_name not in request: + setattr(request, field_name, str(uuid.uuid4())) + else: + if not getattr(request, field_name): + setattr(request, field_name, str(uuid.uuid4())) diff --git a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py index 39a37ded1ed1..c43c51df029b 100644 --- a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py +++ b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py @@ -149,3 +149,112 @@ def test__get_api_endpoint_mtls_universe_mismatch(): default_mtls_endpoint="foo.mtls.googleapis.com", default_endpoint_template="foo.{UNIVERSE_DOMAIN}", ) + + +@mock.patch( + "google.api_core.gapic_v1.client_helpers._use_client_cert_effective" +) +@mock.patch.dict(os.environ, clear=True) +def test__read_environment_variables(mock_effective): + mock_effective.return_value = True + os.environ["GOOGLE_API_USE_MTLS_ENDPOINT"] = "always" + os.environ["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] = "custom.com" + + cert, mtls, domain = client_helpers._read_environment_variables() + assert cert is True + assert mtls == "always" + assert domain == "custom.com" + + +@mock.patch.dict(os.environ, clear=True) +def test__read_environment_variables_invalid_mtls(): + os.environ["GOOGLE_API_USE_MTLS_ENDPOINT"] = "invalid" + with pytest.raises( + MutualTLSChannelError, match="must be `never`, `auto` or `always`" + ): + client_helpers._read_environment_variables() + + +@mock.patch("google.auth.transport.mtls.has_default_client_cert_source") +@mock.patch("google.auth.transport.mtls.default_client_cert_source") +def test__get_client_cert_source(mock_default, mock_has_default): + mock_default.return_value = b"default_cert" + mock_has_default.return_value = True + + # When use_cert_flag is False, return None + assert client_helpers._get_client_cert_source(b"provided", False) is None + + # When provided_cert_source is given, return provided + assert ( + client_helpers._get_client_cert_source(b"provided", True) + == b"provided" + ) + + # When no provided cert but default is available + assert ( + client_helpers._get_client_cert_source(None, True) == b"default_cert" + ) + + +def test__get_universe_domain(): + # client_universe_domain takes precedence + assert ( + client_helpers._get_universe_domain( + "client.com", "env.com", "default.com" + ) + == "client.com" + ) + + # env takes precedence over default + assert ( + client_helpers._get_universe_domain(None, "env.com", "default.com") + == "env.com" + ) + + # fallback to default + assert ( + client_helpers._get_universe_domain(None, None, "default.com") + == "default.com" + ) + + +def test__get_universe_domain_empty(): + with pytest.raises(ValueError, match="cannot be an empty string"): + client_helpers._get_universe_domain("", None, "default.com") + + +def test__setup_request_id(): + import uuid + + # test dict request + req = {} + client_helpers._setup_request_id(req, "request_id", True) + assert "request_id" in req + uuid_str = req["request_id"] + uuid.UUID(uuid_str) # verify it is a valid UUID + + # test dict request when already set + req = {"request_id": "existing"} + client_helpers._setup_request_id(req, "request_id", True) + assert req["request_id"] == "existing" + + class DummyRequest: + def __init__(self): + self.request_id = "" + + def HasField(self, field_name): + if not hasattr(self, field_name): + raise ValueError() + return bool(getattr(self, field_name)) + + # test object request proto3 optional true + req_obj = DummyRequest() + client_helpers._setup_request_id(req_obj, "request_id", True) + assert req_obj.request_id != "" + uuid.UUID(req_obj.request_id) + + # test object request proto3 optional false + req_obj2 = DummyRequest() + client_helpers._setup_request_id(req_obj2, "request_id", False) + assert req_obj2.request_id != "" + uuid.UUID(req_obj2.request_id) From 09243e92724d464ea4b4cd1732a61dc5c75ff394 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 22:41:51 +0000 Subject: [PATCH 15/17] test: mock missing google-auth mtls attributes --- .../tests/unit/gapic/test_client_helpers.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py index c43c51df029b..658ecb82f5ca 100644 --- a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py +++ b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py @@ -175,8 +175,12 @@ def test__read_environment_variables_invalid_mtls(): client_helpers._read_environment_variables() -@mock.patch("google.auth.transport.mtls.has_default_client_cert_source") -@mock.patch("google.auth.transport.mtls.default_client_cert_source") +@mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", create=True +) +@mock.patch( + "google.auth.transport.mtls.default_client_cert_source", create=True +) def test__get_client_cert_source(mock_default, mock_has_default): mock_default.return_value = b"default_cert" mock_has_default.return_value = True From be2b472a773f28800469ca53011769713e426d57 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 6 Jul 2026 22:48:15 +0000 Subject: [PATCH 16/17] style: remove manual 79-char line breaks to satisfy black --- .../api_core/gapic_v1/client_helpers.py | 12 ++----- .../tests/unit/gapic/test_client_helpers.py | 31 +++++-------------- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index 82aad16b1580..0bc8d8cd1ae1 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -126,9 +126,7 @@ def _get_api_endpoint( ) return default_mtls_endpoint else: - return default_endpoint_template.format( - UNIVERSE_DOMAIN=universe_domain - ) + return default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) def _read_environment_variables() -> Tuple[bool, str, Optional[str]]: @@ -147,9 +145,7 @@ def _read_environment_variables() -> Tuple[bool, str, Optional[str]]: ["auto", "never", "always"]. """ use_client_cert = _use_client_cert_effective() - use_mtls_endpoint = os.getenv( - "GOOGLE_API_USE_MTLS_ENDPOINT", "auto" - ).lower() + use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") if use_mtls_endpoint not in ("auto", "never", "always"): raise MutualTLSChannelError( @@ -214,9 +210,7 @@ def _get_universe_domain( return universe_domain -def _setup_request_id( - request: Any, field_name: str, is_proto3_optional: bool -) -> None: +def _setup_request_id(request: Any, field_name: str, is_proto3_optional: bool) -> None: """Populate a UUID4 field in the request if it is not already set. Args: diff --git a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py index 658ecb82f5ca..1c6928cfe010 100644 --- a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py +++ b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py @@ -151,9 +151,7 @@ def test__get_api_endpoint_mtls_universe_mismatch(): ) -@mock.patch( - "google.api_core.gapic_v1.client_helpers._use_client_cert_effective" -) +@mock.patch("google.api_core.gapic_v1.client_helpers._use_client_cert_effective") @mock.patch.dict(os.environ, clear=True) def test__read_environment_variables(mock_effective): mock_effective.return_value = True @@ -175,12 +173,8 @@ def test__read_environment_variables_invalid_mtls(): client_helpers._read_environment_variables() -@mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", create=True -) -@mock.patch( - "google.auth.transport.mtls.default_client_cert_source", create=True -) +@mock.patch("google.auth.transport.mtls.has_default_client_cert_source", create=True) +@mock.patch("google.auth.transport.mtls.default_client_cert_source", create=True) def test__get_client_cert_source(mock_default, mock_has_default): mock_default.return_value = b"default_cert" mock_has_default.return_value = True @@ -189,36 +183,27 @@ def test__get_client_cert_source(mock_default, mock_has_default): assert client_helpers._get_client_cert_source(b"provided", False) is None # When provided_cert_source is given, return provided - assert ( - client_helpers._get_client_cert_source(b"provided", True) - == b"provided" - ) + assert client_helpers._get_client_cert_source(b"provided", True) == b"provided" # When no provided cert but default is available - assert ( - client_helpers._get_client_cert_source(None, True) == b"default_cert" - ) + assert client_helpers._get_client_cert_source(None, True) == b"default_cert" def test__get_universe_domain(): # client_universe_domain takes precedence assert ( - client_helpers._get_universe_domain( - "client.com", "env.com", "default.com" - ) + client_helpers._get_universe_domain("client.com", "env.com", "default.com") == "client.com" ) # env takes precedence over default assert ( - client_helpers._get_universe_domain(None, "env.com", "default.com") - == "env.com" + client_helpers._get_universe_domain(None, "env.com", "default.com") == "env.com" ) # fallback to default assert ( - client_helpers._get_universe_domain(None, None, "default.com") - == "default.com" + client_helpers._get_universe_domain(None, None, "default.com") == "default.com" ) From d386dfe73a5c389ed5d2fdea96c18b4cf4bb4fd4 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Tue, 7 Jul 2026 19:24:23 +0000 Subject: [PATCH 17/17] refactor: remove underscore prefix from gapic client helpers --- .../api_core/gapic_v1/client_helpers.py | 26 +++-- .../tests/unit/gapic/test_client_helpers.py | 107 ++++++++++-------- 2 files changed, 76 insertions(+), 57 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py index 0bc8d8cd1ae1..0ae5929b6a93 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_helpers.py @@ -30,7 +30,7 @@ ) -def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: +def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint. Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to @@ -60,7 +60,7 @@ def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") -def _use_client_cert_effective() -> bool: +def use_client_cert_effective() -> bool: """Returns whether client certificate should be used for mTLS if the google-auth version supports should_use_client_cert automatic mTLS enablement. @@ -91,7 +91,7 @@ def _use_client_cert_effective() -> bool: return use_client_cert_str == "true" -def _get_api_endpoint( +def get_api_endpoint( api_override: Optional[str], client_cert_source: Optional[Any], universe_domain: str, @@ -126,10 +126,12 @@ def _get_api_endpoint( ) return default_mtls_endpoint else: - return default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) + return default_endpoint_template.format( + UNIVERSE_DOMAIN=universe_domain + ) -def _read_environment_variables() -> Tuple[bool, str, Optional[str]]: +def read_environment_variables() -> Tuple[bool, str, Optional[str]]: """Returns the environment variables used by the client. Returns: @@ -144,8 +146,10 @@ def _read_environment_variables() -> Tuple[bool, str, Optional[str]]: GOOGLE_API_USE_MTLS_ENDPOINT is not any of ["auto", "never", "always"]. """ - use_client_cert = _use_client_cert_effective() - use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() + use_client_cert = use_client_cert_effective() + use_mtls_endpoint = os.getenv( + "GOOGLE_API_USE_MTLS_ENDPOINT", "auto" + ).lower() universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") if use_mtls_endpoint not in ("auto", "never", "always"): raise MutualTLSChannelError( @@ -155,7 +159,7 @@ def _read_environment_variables() -> Tuple[bool, str, Optional[str]]: return use_client_cert, use_mtls_endpoint, universe_domain_env -def _get_client_cert_source( +def get_client_cert_source( provided_cert_source: Optional[Any], use_cert_flag: bool ) -> Optional[Any]: """Return the client cert source to be used by the client. @@ -180,7 +184,7 @@ def _get_client_cert_source( return client_cert_source -def _get_universe_domain( +def get_universe_domain( client_universe_domain: Optional[str], universe_domain_env: Optional[str], default_universe: str, @@ -210,7 +214,9 @@ def _get_universe_domain( return universe_domain -def _setup_request_id(request: Any, field_name: str, is_proto3_optional: bool) -> None: +def setup_request_id( + request: Any, field_name: str, is_proto3_optional: bool +) -> None: """Populate a UUID4 field in the request if it is not already set. Args: diff --git a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py index 1c6928cfe010..db7360dc193a 100644 --- a/packages/google-api-core/tests/unit/gapic/test_client_helpers.py +++ b/packages/google-api-core/tests/unit/gapic/test_client_helpers.py @@ -21,69 +21,69 @@ from google.auth.exceptions import MutualTLSChannelError -def test__get_default_mtls_endpoint(): +def test_get_default_mtls_endpoint(): # Test valid API endpoints assert ( - client_helpers._get_default_mtls_endpoint("foo.googleapis.com") + client_helpers.get_default_mtls_endpoint("foo.googleapis.com") == "foo.mtls.googleapis.com" ) assert ( - client_helpers._get_default_mtls_endpoint("foo.sandbox.googleapis.com") + client_helpers.get_default_mtls_endpoint("foo.sandbox.googleapis.com") == "foo.mtls.sandbox.googleapis.com" ) # Test endpoints that shouldn't be converted assert ( - client_helpers._get_default_mtls_endpoint("foo.mtls.googleapis.com") + client_helpers.get_default_mtls_endpoint("foo.mtls.googleapis.com") == "foo.mtls.googleapis.com" ) - assert client_helpers._get_default_mtls_endpoint("foo.com") == "foo.com" + assert client_helpers.get_default_mtls_endpoint("foo.com") == "foo.com" # Test empty/None endpoints - assert client_helpers._get_default_mtls_endpoint("") == "" - assert client_helpers._get_default_mtls_endpoint(None) is None + assert client_helpers.get_default_mtls_endpoint("") == "" + assert client_helpers.get_default_mtls_endpoint(None) is None @mock.patch("google.auth.transport.mtls.should_use_client_cert", autospec=True) -def test__use_client_cert_effective_with_google_auth(mock_method): +def test_use_client_cert_effective_with_google_auth(mock_method): # Test when google-auth supports the method mock_method.return_value = True - assert client_helpers._use_client_cert_effective() is True + assert client_helpers.use_client_cert_effective() is True mock_method.return_value = False - assert client_helpers._use_client_cert_effective() is False + assert client_helpers.use_client_cert_effective() is False @mock.patch.dict(os.environ, {}, clear=True) -def test__use_client_cert_effective_fallback(): +def test_use_client_cert_effective_fallback(): # We must patch hasattr to simulate google-auth lacking the method with mock.patch( "google.api_core.gapic_v1.client_helpers.hasattr", return_value=False ): # Default is false - assert client_helpers._use_client_cert_effective() is False + assert client_helpers.use_client_cert_effective() is False env_true = {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"} with mock.patch.dict(os.environ, env_true): - assert client_helpers._use_client_cert_effective() is True + assert client_helpers.use_client_cert_effective() is True with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"} ): - assert client_helpers._use_client_cert_effective() is False + assert client_helpers.use_client_cert_effective() is False with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "invalid"} ): match_str = "must be either `true` or `false`" with pytest.raises(ValueError, match=match_str): - client_helpers._use_client_cert_effective() + client_helpers.use_client_cert_effective() -def test__get_api_endpoint_override(): +def test_get_api_endpoint_override(): # If api_override is provided, it should be returned # regardless of other args - endpoint = client_helpers._get_api_endpoint( + endpoint = client_helpers.get_api_endpoint( api_override="custom.endpoint.com", client_cert_source=None, universe_domain="googleapis.com", @@ -95,9 +95,9 @@ def test__get_api_endpoint_override(): assert endpoint == "custom.endpoint.com" -def test__get_api_endpoint_mtls_always(): +def test_get_api_endpoint_mtls_always(): # use_mtls_endpoint == "always" should use the default mtls endpoint - endpoint = client_helpers._get_api_endpoint( + endpoint = client_helpers.get_api_endpoint( api_override=None, client_cert_source=None, universe_domain="googleapis.com", @@ -109,9 +109,9 @@ def test__get_api_endpoint_mtls_always(): assert endpoint == "foo.mtls.googleapis.com" -def test__get_api_endpoint_mtls_auto_with_cert(): +def test_get_api_endpoint_mtls_auto_with_cert(): # "auto" with client_cert_source should use mtls - endpoint = client_helpers._get_api_endpoint( + endpoint = client_helpers.get_api_endpoint( api_override=None, client_cert_source=mock.Mock(), universe_domain="googleapis.com", @@ -123,9 +123,9 @@ def test__get_api_endpoint_mtls_auto_with_cert(): assert endpoint == "foo.mtls.googleapis.com" -def test__get_api_endpoint_mtls_auto_no_cert(): +def test_get_api_endpoint_mtls_auto_no_cert(): # "auto" without client_cert_source should use the default template - endpoint = client_helpers._get_api_endpoint( + endpoint = client_helpers.get_api_endpoint( api_override=None, client_cert_source=None, universe_domain="googleapis.com", @@ -137,10 +137,10 @@ def test__get_api_endpoint_mtls_auto_no_cert(): assert endpoint == "foo.googleapis.com" -def test__get_api_endpoint_mtls_universe_mismatch(): +def test_get_api_endpoint_mtls_universe_mismatch(): # mTLS is only supported in the default universe with pytest.raises(MutualTLSChannelError, match="mTLS is not supported"): - client_helpers._get_api_endpoint( + client_helpers.get_api_endpoint( api_override=None, client_cert_source=mock.Mock(), universe_domain="custom-universe.com", @@ -151,80 +151,93 @@ def test__get_api_endpoint_mtls_universe_mismatch(): ) -@mock.patch("google.api_core.gapic_v1.client_helpers._use_client_cert_effective") +@mock.patch( + "google.api_core.gapic_v1.client_helpers.use_client_cert_effective" +) @mock.patch.dict(os.environ, clear=True) -def test__read_environment_variables(mock_effective): +def test_read_environment_variables(mock_effective): mock_effective.return_value = True os.environ["GOOGLE_API_USE_MTLS_ENDPOINT"] = "always" os.environ["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] = "custom.com" - cert, mtls, domain = client_helpers._read_environment_variables() + cert, mtls, domain = client_helpers.read_environment_variables() assert cert is True assert mtls == "always" assert domain == "custom.com" @mock.patch.dict(os.environ, clear=True) -def test__read_environment_variables_invalid_mtls(): +def test_read_environment_variables_invalid_mtls(): os.environ["GOOGLE_API_USE_MTLS_ENDPOINT"] = "invalid" with pytest.raises( MutualTLSChannelError, match="must be `never`, `auto` or `always`" ): - client_helpers._read_environment_variables() + client_helpers.read_environment_variables() -@mock.patch("google.auth.transport.mtls.has_default_client_cert_source", create=True) -@mock.patch("google.auth.transport.mtls.default_client_cert_source", create=True) -def test__get_client_cert_source(mock_default, mock_has_default): +@mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", create=True +) +@mock.patch( + "google.auth.transport.mtls.default_client_cert_source", create=True +) +def test_get_client_cert_source(mock_default, mock_has_default): mock_default.return_value = b"default_cert" mock_has_default.return_value = True # When use_cert_flag is False, return None - assert client_helpers._get_client_cert_source(b"provided", False) is None + assert client_helpers.get_client_cert_source(b"provided", False) is None # When provided_cert_source is given, return provided - assert client_helpers._get_client_cert_source(b"provided", True) == b"provided" + assert ( + client_helpers.get_client_cert_source(b"provided", True) + == b"provided" + ) # When no provided cert but default is available - assert client_helpers._get_client_cert_source(None, True) == b"default_cert" + assert client_helpers.get_client_cert_source(None, True) == b"default_cert" -def test__get_universe_domain(): +def test_get_universe_domain(): # client_universe_domain takes precedence assert ( - client_helpers._get_universe_domain("client.com", "env.com", "default.com") + client_helpers.get_universe_domain( + "client.com", "env.com", "default.com" + ) == "client.com" ) # env takes precedence over default assert ( - client_helpers._get_universe_domain(None, "env.com", "default.com") == "env.com" + client_helpers.get_universe_domain(None, "env.com", "default.com") + == "env.com" ) # fallback to default assert ( - client_helpers._get_universe_domain(None, None, "default.com") == "default.com" + client_helpers.get_universe_domain(None, None, "default.com") + == "default.com" ) -def test__get_universe_domain_empty(): +def test_get_universe_domain_empty(): with pytest.raises(ValueError, match="cannot be an empty string"): - client_helpers._get_universe_domain("", None, "default.com") + client_helpers.get_universe_domain("", None, "default.com") -def test__setup_request_id(): +def test_setup_request_id(): import uuid # test dict request req = {} - client_helpers._setup_request_id(req, "request_id", True) + client_helpers.setup_request_id(req, "request_id", True) assert "request_id" in req uuid_str = req["request_id"] uuid.UUID(uuid_str) # verify it is a valid UUID # test dict request when already set req = {"request_id": "existing"} - client_helpers._setup_request_id(req, "request_id", True) + client_helpers.setup_request_id(req, "request_id", True) assert req["request_id"] == "existing" class DummyRequest: @@ -238,12 +251,12 @@ def HasField(self, field_name): # test object request proto3 optional true req_obj = DummyRequest() - client_helpers._setup_request_id(req_obj, "request_id", True) + client_helpers.setup_request_id(req_obj, "request_id", True) assert req_obj.request_id != "" uuid.UUID(req_obj.request_id) # test object request proto3 optional false req_obj2 = DummyRequest() - client_helpers._setup_request_id(req_obj2, "request_id", False) + client_helpers.setup_request_id(req_obj2, "request_id", False) assert req_obj2.request_id != "" uuid.UUID(req_obj2.request_id)