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
31from .services.iam_credentials import IAMCredentialsAsyncClient, IAMCredentialsClient
32from .types.common import (
33 GenerateAccessTokenRequest,
34 GenerateAccessTokenResponse,
35 GenerateIdTokenRequest,
36 GenerateIdTokenResponse,
37 SignBlobRequest,
38 SignBlobResponse,
39 SignJwtRequest,
40 SignJwtResponse,
41)
42
43if hasattr(api_core, "check_python_version") and hasattr(
44 api_core, "check_dependency_versions"
45): # pragma: NO COVER
46 api_core.check_python_version("google.cloud.iam_credentials_v1") # type: ignore
47 api_core.check_dependency_versions("google.cloud.iam_credentials_v1") # type: ignore
48else: # pragma: NO COVER
49 # An older version of api_core is installed which does not define the
50 # functions above. We do equivalent checks manually.
51 try:
52 import sys
53 import warnings
54
55 _py_version_str = sys.version.split()[0]
56 _package_label = "google.cloud.iam_credentials_v1"
57 if sys.version_info < (3, 9):
58 warnings.warn(
59 "You are using a non-supported Python version "
60 + f"({_py_version_str}). Google will not post any further "
61 + f"updates to {_package_label} supporting this Python version. "
62 + "Please upgrade to the latest Python version, or at "
63 + f"least to Python 3.9, and then update {_package_label}.",
64 FutureWarning,
65 )
66 if sys.version_info[:2] == (3, 9):
67 warnings.warn(
68 f"You are using a Python version ({_py_version_str}) "
69 + f"which Google will stop supporting in {_package_label} in "
70 + "January 2026. Please "
71 + "upgrade to the latest Python version, or at "
72 + "least to Python 3.10, before then, and "
73 + f"then update {_package_label}.",
74 FutureWarning,
75 )
76
77 def parse_version_to_tuple(version_string: str):
78 """Safely converts a semantic version string to a comparable tuple of integers.
79 Example: "4.25.8" -> (4, 25, 8)
80 Ignores non-numeric parts and handles common version formats.
81 Args:
82 version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
83 Returns:
84 Tuple of integers for the parsed version string.
85 """
86 parts = []
87 for part in version_string.split("."):
88 try:
89 parts.append(int(part))
90 except ValueError:
91 # If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
92 # This is a simplification compared to 'packaging.parse_version', but sufficient
93 # for comparing strictly numeric semantic versions.
94 break
95 return tuple(parts)
96
97 def _get_version(dependency_name):
98 try:
99 version_string: str = metadata.version(dependency_name)
100 parsed_version = parse_version_to_tuple(version_string)
101 return (parsed_version, version_string)
102 except Exception:
103 # Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
104 # or errors during parse_version_to_tuple
105 return (None, "--")
106
107 _dependency_package = "google.protobuf"
108 _next_supported_version = "4.25.8"
109 _next_supported_version_tuple = (4, 25, 8)
110 _recommendation = " (we recommend 6.x)"
111 (_version_used, _version_used_string) = _get_version(_dependency_package)
112 if _version_used and _version_used < _next_supported_version_tuple:
113 warnings.warn(
114 f"Package {_package_label} depends on "
115 + f"{_dependency_package}, currently installed at version "
116 + f"{_version_used_string}. Future updates to "
117 + f"{_package_label} will require {_dependency_package} at "
118 + f"version {_next_supported_version} or higher{_recommendation}."
119 + " Please ensure "
120 + "that either (a) your Python environment doesn't pin the "
121 + f"version of {_dependency_package}, so that updates to "
122 + f"{_package_label} can require the higher version, or "
123 + "(b) you manually update your Python environment to use at "
124 + f"least version {_next_supported_version} of "
125 + f"{_dependency_package}.",
126 FutureWarning,
127 )
128 except Exception:
129 warnings.warn(
130 "Could not determine the version of Python "
131 + "currently being used. To continue receiving "
132 + "updates for {_package_label}, ensure you are "
133 + "using a supported version of Python; see "
134 + "https://devguide.python.org/versions/"
135 )
136
137__all__ = (
138 "IAMCredentialsAsyncClient",
139 "GenerateAccessTokenRequest",
140 "GenerateAccessTokenResponse",
141 "GenerateIdTokenRequest",
142 "GenerateIdTokenResponse",
143 "IAMCredentialsClient",
144 "SignBlobRequest",
145 "SignBlobResponse",
146 "SignJwtRequest",
147 "SignJwtResponse",
148)