1# -*- coding: utf-8 -*-
2# Copyright 2025 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16import sys
17
18import google.api_core as api_core
19
20from google.cloud.iam_credentials_v1 import gapic_version as package_version
21
22__version__ = package_version.__version__
23
24if sys.version_info >= (3, 8): # pragma: NO COVER
25 from importlib import metadata
26else: # pragma: NO COVER
27 # TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
28 # this code path once we drop support for Python 3.7
29 import importlib_metadata as metadata
30
31
32from .services.iam_credentials import IAMCredentialsAsyncClient, IAMCredentialsClient
33from .types.common import (
34 GenerateAccessTokenRequest,
35 GenerateAccessTokenResponse,
36 GenerateIdTokenRequest,
37 GenerateIdTokenResponse,
38 SignBlobRequest,
39 SignBlobResponse,
40 SignJwtRequest,
41 SignJwtResponse,
42)
43
44if hasattr(api_core, "check_python_version") and hasattr(
45 api_core, "check_dependency_versions"
46): # pragma: NO COVER
47 api_core.check_python_version("google.cloud.iam_credentials_v1") # type: ignore
48 api_core.check_dependency_versions("google.cloud.iam_credentials_v1") # type: ignore
49else: # pragma: NO COVER
50 # An older version of api_core is installed which does not define the
51 # functions above. We do equivalent checks manually.
52 try:
53 import sys
54 import warnings
55
56 _py_version_str = sys.version.split()[0]
57 _package_label = "google.cloud.iam_credentials_v1"
58 if sys.version_info < (3, 9):
59 warnings.warn(
60 "You are using a non-supported Python version "
61 + f"({_py_version_str}). Google will not post any further "
62 + f"updates to {_package_label} supporting this Python version. "
63 + "Please upgrade to the latest Python version, or at "
64 + f"least to Python 3.9, and then update {_package_label}.",
65 FutureWarning,
66 )
67 if sys.version_info[:2] == (3, 9):
68 warnings.warn(
69 f"You are using a Python version ({_py_version_str}) "
70 + f"which Google will stop supporting in {_package_label} in "
71 + "January 2026. Please "
72 + "upgrade to the latest Python version, or at "
73 + "least to Python 3.10, before then, and "
74 + f"then update {_package_label}.",
75 FutureWarning,
76 )
77
78 def parse_version_to_tuple(version_string: str):
79 """Safely converts a semantic version string to a comparable tuple of integers.
80 Example: "4.25.8" -> (4, 25, 8)
81 Ignores non-numeric parts and handles common version formats.
82 Args:
83 version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
84 Returns:
85 Tuple of integers for the parsed version string.
86 """
87 parts = []
88 for part in version_string.split("."):
89 try:
90 parts.append(int(part))
91 except ValueError:
92 # If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
93 # This is a simplification compared to 'packaging.parse_version', but sufficient
94 # for comparing strictly numeric semantic versions.
95 break
96 return tuple(parts)
97
98 def _get_version(dependency_name):
99 try:
100 version_string: str = metadata.version(dependency_name)
101 parsed_version = parse_version_to_tuple(version_string)
102 return (parsed_version, version_string)
103 except Exception:
104 # Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
105 # or errors during parse_version_to_tuple
106 return (None, "--")
107
108 _dependency_package = "google.protobuf"
109 _next_supported_version = "4.25.8"
110 _next_supported_version_tuple = (4, 25, 8)
111 _recommendation = " (we recommend 6.x)"
112 (_version_used, _version_used_string) = _get_version(_dependency_package)
113 if _version_used and _version_used < _next_supported_version_tuple:
114 warnings.warn(
115 f"Package {_package_label} depends on "
116 + f"{_dependency_package}, currently installed at version "
117 + f"{_version_used_string}. Future updates to "
118 + f"{_package_label} will require {_dependency_package} at "
119 + f"version {_next_supported_version} or higher{_recommendation}."
120 + " Please ensure "
121 + "that either (a) your Python environment doesn't pin the "
122 + f"version of {_dependency_package}, so that updates to "
123 + f"{_package_label} can require the higher version, or "
124 + "(b) you manually update your Python environment to use at "
125 + f"least version {_next_supported_version} of "
126 + f"{_dependency_package}.",
127 FutureWarning,
128 )
129 except Exception:
130 warnings.warn(
131 "Could not determine the version of Python "
132 + "currently being used. To continue receiving "
133 + "updates for {_package_label}, ensure you are "
134 + "using a supported version of Python; see "
135 + "https://devguide.python.org/versions/"
136 )
137
138__all__ = (
139 "IAMCredentialsAsyncClient",
140 "GenerateAccessTokenRequest",
141 "GenerateAccessTokenResponse",
142 "GenerateIdTokenRequest",
143 "GenerateIdTokenResponse",
144 "IAMCredentialsClient",
145 "SignBlobRequest",
146 "SignBlobResponse",
147 "SignJwtRequest",
148 "SignJwtResponse",
149)