Skip to content

Commit b4824c5

Browse files
fix: drop packaging and pkg_resources (#2477)
Co-authored-by: Daniel Sanche <[email protected]>
1 parent 4748760 commit b4824c5

File tree

17 files changed

+587
-483
lines changed

17 files changed

+587
-483
lines changed

gapic/templates/%namespace/%name_%version/%sub/__init__.py.j2

Lines changed: 65 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,46 @@
55
{% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %}
66
from {{package_path}} import gapic_version as package_version
77

8+
import google.api_core as api_core
9+
import sys
10+
811
__version__ = package_version.__version__
912

13+
if sys.version_info >= (3, 8): # pragma: NO COVER
14+
from importlib import metadata
15+
else: # pragma: NO COVER
16+
# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
17+
# this code path once we drop support for Python 3.7
18+
import importlib_metadata as metadata
1019

11-
import google.api_core as api_core
20+
{# Import subpackages. -#}
21+
{% for subpackage, _ in api.subpackages|dictsort %}
22+
from . import {{ subpackage }}
23+
{% endfor %}
24+
25+
{# Import services for this package. -#}
26+
{% for service in api.services.values()|sort(attribute='name')
27+
if service.meta.address.subpackage == api.subpackage_view %}
28+
from .services.{{ service.name|snake_case }} import {{ service.client_name }}
29+
{% if 'grpc' in opts.transport %}
30+
from .services.{{ service.name|snake_case }} import {{ service.async_client_name }}
31+
{% endif %}
32+
{% endfor %}
33+
34+
{# Import messages and enums from each proto.
35+
It is safe to import all of the messages into the same namespace here,
36+
because protocol buffers itself enforces selector uniqueness within
37+
a proto package.
38+
-#}
39+
{% for proto in api.protos.values()|sort(attribute='name')
40+
if proto.meta.address.subpackage == api.subpackage_view %}
41+
{% for message in proto.messages.values()|sort(attribute='name') %}
42+
from .types.{{ proto.module_name }} import {{ message.name }}
43+
{% endfor %}
44+
{% for enum in proto.enums.values()|sort(attribute='name') %}
45+
from .types.{{ proto.module_name }} import {{ enum.name }}
46+
{% endfor %}
47+
{% endfor %}
1248

1349
if hasattr(api_core, "check_python_version") and hasattr(api_core, "check_dependency_versions"): # pragma: NO COVER
1450
{# TODO(api_core): remove `type:ignore` below when minimum version of api_core makes the else clause unnecessary. #}
@@ -45,27 +81,35 @@ else: # pragma: NO COVER
4581
f"then update {_package_label}.",
4682
FutureWarning)
4783

48-
from packaging.version import parse as parse_version
49-
50-
if sys.version_info < (3, 8):
51-
import pkg_resources
52-
53-
def _get_version(dependency_name):
54-
try:
55-
version_string = pkg_resources.get_distribution(dependency_name).version
56-
return (parse_version(version_string), version_string)
57-
except pkg_resources.DistributionNotFound:
58-
return (None, "--")
59-
else:
60-
from importlib import metadata
61-
62-
def _get_version(dependency_name):
84+
def parse_version_to_tuple(version_string: str):
85+
"""Safely converts a semantic version string to a comparable tuple of integers.
86+
Example: "4.25.8" -> (4, 25, 8)
87+
Ignores non-numeric parts and handles common version formats.
88+
Args:
89+
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
90+
Returns:
91+
Tuple of integers for the parsed version string.
92+
"""
93+
parts = []
94+
for part in version_string.split("."):
6395
try:
64-
version_string = metadata.version("requests")
65-
parsed_version = parse_version(version_string)
66-
return (parsed_version.release, version_string)
67-
except metadata.PackageNotFoundError:
68-
return (None, "--")
96+
parts.append(int(part))
97+
except ValueError:
98+
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
99+
# This is a simplification compared to 'packaging.parse_version', but sufficient
100+
# for comparing strictly numeric semantic versions.
101+
break
102+
return tuple(parts)
103+
104+
def _get_version(dependency_name):
105+
try:
106+
version_string: str = metadata.version(dependency_name)
107+
parsed_version = parse_version_to_tuple(version_string)
108+
return (parsed_version, version_string)
109+
except Exception:
110+
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
111+
# or errors during parse_version_to_tuple
112+
return (None, "--")
69113

70114
_dependency_package = "google.protobuf"
71115
_next_supported_version = "4.25.8"
@@ -93,35 +137,6 @@ else: # pragma: NO COVER
93137
"using a supported version of Python; see " +
94138
"https://devguide.python.org/versions/")
95139

96-
{# Import subpackages. -#}
97-
{% for subpackage, _ in api.subpackages|dictsort %}
98-
from . import {{ subpackage }}
99-
{% endfor %}
100-
101-
{# Import services for this package. -#}
102-
{% for service in api.services.values()|sort(attribute='name')
103-
if service.meta.address.subpackage == api.subpackage_view %}
104-
from .services.{{ service.name|snake_case }} import {{ service.client_name }}
105-
{% if 'grpc' in opts.transport %}
106-
from .services.{{ service.name|snake_case }} import {{ service.async_client_name }}
107-
{% endif %}
108-
{% endfor %}
109-
110-
{# Import messages and enums from each proto.
111-
It is safe to import all of the messages into the same namespace here,
112-
because protocol buffers itself enforces selector uniqueness within
113-
a proto package.
114-
-#}
115-
{% for proto in api.protos.values()|sort(attribute='name')
116-
if proto.meta.address.subpackage == api.subpackage_view %}
117-
{% for message in proto.messages.values()|sort(attribute='name') %}
118-
from .types.{{ proto.module_name }} import {{ message.name }}
119-
{% endfor %}
120-
{% for enum in proto.enums.values()|sort(attribute='name') %}
121-
from .types.{{ proto.module_name }} import {{ enum.name }}
122-
{% endfor %}
123-
{% endfor %}
124-
125140
{# Define __all__.
126141
This requires the full set of imported names, so we iterate over
127142
them again.

gapic/templates/setup.py.j2

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ dependencies = [
3939
"google-auth >= 2.14.1, <3.0.0,!=2.24.0,!=2.25.0",
4040
"grpcio >= 1.33.2, < 2.0.0",
4141
"grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
42-
"packaging", # TODO: Remove once we require versions of api core that include this
4342
"proto-plus >= 1.22.3, <2.0.0",
4443
"proto-plus >= 1.25.0, <2.0.0; python_version >= '3.13'",
4544
{# Explicitly exclude protobuf versions mentioned in https://cloud.google.com/support/bulletins#GCP-2022-019 #}

noxfile.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ def unit(session):
6464
"pyfakefs",
6565
"grpcio-status",
6666
"proto-plus",
67-
"setuptools", # TODO: Remove when not needed in __init__.py.j2
68-
"packaging", # TODO: Remove when not needed in __init__.py.j2
6967
)
7068
session.install("-e", ".")
7169
session.run(
@@ -501,8 +499,6 @@ def run_showcase_unit_tests(session, fail_under=100, rest_async_io_enabled=False
501499
"pytest-xdist",
502500
"asyncmock; python_version < '3.8'",
503501
"pytest-asyncio",
504-
"setuptools", # TODO: Remove when not needed in __init__.py.j2
505-
"packaging", # TODO: Remove when not needed in __init__.py.j2
506502
)
507503
# Run the tests.
508504
# NOTE: async rest is not supported against the minimum supported version of google-api-core.
@@ -617,8 +613,6 @@ def showcase_mypy(
617613
"types-protobuf",
618614
"types-requests",
619615
"types-dataclasses",
620-
"setuptools", # TODO: Remove when not needed in __init__.py.j2
621-
"packaging", # TODO: Remove when not needed in __init__.py.j2
622616
)
623617

624618
with showcase_library(session, templates=templates, other_opts=other_opts) as lib:
@@ -749,8 +743,6 @@ def mypy(session):
749743
"types-PyYAML",
750744
"types-dataclasses",
751745
"click==8.1.3",
752-
"setuptools", # TODO: Remove when not needed in __init__.py.j2
753-
"packaging", # TODO: Remove when not needed in __init__.py.j2
754746
)
755747
session.install(".")
756748
session.run("mypy", "-p", "gapic")

tests/integration/goldens/asset/google/cloud/asset_v1/__init__.py

Lines changed: 92 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -15,86 +15,17 @@
1515
#
1616
from google.cloud.asset_v1 import gapic_version as package_version
1717

18-
__version__ = package_version.__version__
19-
20-
2118
import google.api_core as api_core
19+
import sys
2220

23-
if hasattr(api_core, "check_python_version") and hasattr(api_core, "check_dependency_versions"): # pragma: NO COVER
24-
api_core.check_python_version("google.cloud.asset_v1") # type: ignore
25-
api_core.check_dependency_versions("google.cloud.asset_v1") # type: ignore
26-
else: # pragma: NO COVER
27-
# An older version of api_core is installed which does not define the
28-
# functions above. We do equivalent checks manually.
29-
try:
30-
import warnings
31-
import sys
32-
33-
_py_version_str = sys.version.split()[0]
34-
_package_label = "google.cloud.asset_v1"
35-
if sys.version_info < (3, 9):
36-
warnings.warn("You are using a non-supported Python version " +
37-
f"({_py_version_str}). Google will not post any further " +
38-
f"updates to {_package_label} supporting this Python version. " +
39-
"Please upgrade to the latest Python version, or at " +
40-
f"least to Python 3.9, and then update {_package_label}.",
41-
FutureWarning)
42-
if sys.version_info[:2] == (3, 9):
43-
warnings.warn(f"You are using a Python version ({_py_version_str}) " +
44-
f"which Google will stop supporting in {_package_label} in " +
45-
"January 2026. Please " +
46-
"upgrade to the latest Python version, or at " +
47-
"least to Python 3.10, before then, and " +
48-
f"then update {_package_label}.",
49-
FutureWarning)
50-
51-
from packaging.version import parse as parse_version
52-
53-
if sys.version_info < (3, 8):
54-
import pkg_resources
55-
56-
def _get_version(dependency_name):
57-
try:
58-
version_string = pkg_resources.get_distribution(dependency_name).version
59-
return (parse_version(version_string), version_string)
60-
except pkg_resources.DistributionNotFound:
61-
return (None, "--")
62-
else:
63-
from importlib import metadata
64-
65-
def _get_version(dependency_name):
66-
try:
67-
version_string = metadata.version("requests")
68-
parsed_version = parse_version(version_string)
69-
return (parsed_version.release, version_string)
70-
except metadata.PackageNotFoundError:
71-
return (None, "--")
21+
__version__ = package_version.__version__
7222

73-
_dependency_package = "google.protobuf"
74-
_next_supported_version = "4.25.8"
75-
_next_supported_version_tuple = (4, 25, 8)
76-
_recommendation = " (we recommend 6.x)"
77-
(_version_used, _version_used_string) = _get_version(_dependency_package)
78-
if _version_used and _version_used < _next_supported_version_tuple:
79-
warnings.warn(f"Package {_package_label} depends on " +
80-
f"{_dependency_package}, currently installed at version " +
81-
f"{_version_used_string}. Future updates to " +
82-
f"{_package_label} will require {_dependency_package} at " +
83-
f"version {_next_supported_version} or higher{_recommendation}." +
84-
" Please ensure " +
85-
"that either (a) your Python environment doesn't pin the " +
86-
f"version of {_dependency_package}, so that updates to " +
87-
f"{_package_label} can require the higher version, or " +
88-
"(b) you manually update your Python environment to use at " +
89-
f"least version {_next_supported_version} of " +
90-
f"{_dependency_package}.",
91-
FutureWarning)
92-
except Exception:
93-
warnings.warn("Could not determine the version of Python " +
94-
"currently being used. To continue receiving " +
95-
"updates for {_package_label}, ensure you are " +
96-
"using a supported version of Python; see " +
97-
"https://devguide.python.org/versions/")
23+
if sys.version_info >= (3, 8): # pragma: NO COVER
24+
from importlib import metadata
25+
else: # pragma: NO COVER
26+
# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
27+
# this code path once we drop support for Python 3.7
28+
import importlib_metadata as metadata
9829

9930

10031
from .services.asset_service import AssetServiceClient
@@ -178,6 +109,90 @@ def _get_version(dependency_name):
178109
from .types.assets import TimeWindow
179110
from .types.assets import VersionedResource
180111

112+
if hasattr(api_core, "check_python_version") and hasattr(api_core, "check_dependency_versions"): # pragma: NO COVER
113+
api_core.check_python_version("google.cloud.asset_v1") # type: ignore
114+
api_core.check_dependency_versions("google.cloud.asset_v1") # type: ignore
115+
else: # pragma: NO COVER
116+
# An older version of api_core is installed which does not define the
117+
# functions above. We do equivalent checks manually.
118+
try:
119+
import warnings
120+
import sys
121+
122+
_py_version_str = sys.version.split()[0]
123+
_package_label = "google.cloud.asset_v1"
124+
if sys.version_info < (3, 9):
125+
warnings.warn("You are using a non-supported Python version " +
126+
f"({_py_version_str}). Google will not post any further " +
127+
f"updates to {_package_label} supporting this Python version. " +
128+
"Please upgrade to the latest Python version, or at " +
129+
f"least to Python 3.9, and then update {_package_label}.",
130+
FutureWarning)
131+
if sys.version_info[:2] == (3, 9):
132+
warnings.warn(f"You are using a Python version ({_py_version_str}) " +
133+
f"which Google will stop supporting in {_package_label} in " +
134+
"January 2026. Please " +
135+
"upgrade to the latest Python version, or at " +
136+
"least to Python 3.10, before then, and " +
137+
f"then update {_package_label}.",
138+
FutureWarning)
139+
140+
def parse_version_to_tuple(version_string: str):
141+
"""Safely converts a semantic version string to a comparable tuple of integers.
142+
Example: "4.25.8" -> (4, 25, 8)
143+
Ignores non-numeric parts and handles common version formats.
144+
Args:
145+
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
146+
Returns:
147+
Tuple of integers for the parsed version string.
148+
"""
149+
parts = []
150+
for part in version_string.split("."):
151+
try:
152+
parts.append(int(part))
153+
except ValueError:
154+
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
155+
# This is a simplification compared to 'packaging.parse_version', but sufficient
156+
# for comparing strictly numeric semantic versions.
157+
break
158+
return tuple(parts)
159+
160+
def _get_version(dependency_name):
161+
try:
162+
version_string: str = metadata.version(dependency_name)
163+
parsed_version = parse_version_to_tuple(version_string)
164+
return (parsed_version, version_string)
165+
except Exception:
166+
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
167+
# or errors during parse_version_to_tuple
168+
return (None, "--")
169+
170+
_dependency_package = "google.protobuf"
171+
_next_supported_version = "4.25.8"
172+
_next_supported_version_tuple = (4, 25, 8)
173+
_recommendation = " (we recommend 6.x)"
174+
(_version_used, _version_used_string) = _get_version(_dependency_package)
175+
if _version_used and _version_used < _next_supported_version_tuple:
176+
warnings.warn(f"Package {_package_label} depends on " +
177+
f"{_dependency_package}, currently installed at version " +
178+
f"{_version_used_string}. Future updates to " +
179+
f"{_package_label} will require {_dependency_package} at " +
180+
f"version {_next_supported_version} or higher{_recommendation}." +
181+
" Please ensure " +
182+
"that either (a) your Python environment doesn't pin the " +
183+
f"version of {_dependency_package}, so that updates to " +
184+
f"{_package_label} can require the higher version, or " +
185+
"(b) you manually update your Python environment to use at " +
186+
f"least version {_next_supported_version} of " +
187+
f"{_dependency_package}.",
188+
FutureWarning)
189+
except Exception:
190+
warnings.warn("Could not determine the version of Python " +
191+
"currently being used. To continue receiving " +
192+
"updates for {_package_label}, ensure you are " +
193+
"using a supported version of Python; see " +
194+
"https://devguide.python.org/versions/")
195+
181196
__all__ = (
182197
'AssetServiceAsyncClient',
183198
'AnalyzeIamPolicyLongrunningMetadata',

tests/integration/goldens/asset/setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
"google-auth >= 2.14.1, <3.0.0,!=2.24.0,!=2.25.0",
4646
"grpcio >= 1.33.2, < 2.0.0",
4747
"grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
48-
"packaging", # TODO: Remove once we require versions of api core that include this
4948
"proto-plus >= 1.22.3, <2.0.0",
5049
"proto-plus >= 1.25.0, <2.0.0; python_version >= '3.13'",
5150
"protobuf>=3.20.2,<7.0.0,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5",

0 commit comments

Comments
 (0)