Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .librarian/state.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:8e2c32496077054105bd06c54a59d6a6694287bc053588e24debe6da6920ad91
image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:234b9d1f2ddb057ed7ac6a38db0bf8163d839c65c6cf88ade52530cddebce59e
libraries:
- id: google-cloud-storage
version: 3.10.1
Expand Down
104 changes: 104 additions & 0 deletions google/cloud/_storage_v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,18 @@
#
from google.cloud._storage_v2 import gapic_version as package_version

import google.api_core as api_core
import sys

__version__ = package_version.__version__

if sys.version_info >= (3, 8): # pragma: NO COVER
from importlib import metadata
else: # pragma: NO COVER
# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
# this code path once we drop support for Python 3.7
import importlib_metadata as metadata
Comment on lines +25 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The general rules state that the library only supports Python versions 3.10 and greater. This else block specifically handles Python 3.7, which contradicts the stated policy. This code path should be removed.

References
  1. The library only supports Python versions 3.10 and greater.



from .services.storage import StorageClient
from .services.storage import StorageAsyncClient
Expand Down Expand Up @@ -78,6 +88,100 @@
from .types.storage import WriteObjectResponse
from .types.storage import WriteObjectSpec

if hasattr(api_core, "check_python_version") and hasattr(
api_core, "check_dependency_versions"
): # pragma: NO COVER
api_core.check_python_version("google.cloud._storage_v2") # type: ignore
api_core.check_dependency_versions("google.cloud._storage_v2") # type: ignore
else: # pragma: NO COVER
# An older version of api_core is installed which does not define the
# functions above. We do equivalent checks manually.
try:
import warnings
import sys

_py_version_str = sys.version.split()[0]
_package_label = "google.cloud._storage_v2"
if sys.version_info < (3, 9):
warnings.warn(
"You are using a non-supported Python version "
+ f"({_py_version_str}). Google will not post any further "
+ f"updates to {_package_label} supporting this Python version. "
+ "Please upgrade to the latest Python version, or at "
+ f"least to Python 3.9, and then update {_package_label}.",
FutureWarning,
)
if sys.version_info[:2] == (3, 9):
warnings.warn(
f"You are using a Python version ({_py_version_str}) "
+ f"which Google will stop supporting in {_package_label} in "
+ "January 2026. Please "
+ "upgrade to the latest Python version, or at "
+ "least to Python 3.10, before then, and "
+ f"then update {_package_label}.",
FutureWarning,
)
Comment on lines +105 to +123
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The general rules state that the library only supports Python versions 3.10 and greater. This code block issues warnings for Python versions < 3.9 and Python 3.9, which are outside the supported range. These warnings are not necessary if the library strictly enforces Python 3.10+.

References
  1. The library only supports Python versions 3.10 and greater.


def parse_version_to_tuple(version_string: str):
"""Safely converts a semantic version string to a comparable tuple of integers.
Example: "4.25.8" -> (4, 25, 8)
Ignores non-numeric parts and handles common version formats.
Args:
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
Returns:
Tuple of integers for the parsed version string.
"""
parts = []
for part in version_string.split("."):
try:
parts.append(int(part))
except ValueError:
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
# This is a simplification compared to 'packaging.parse_version', but sufficient
# for comparing strictly numeric semantic versions.
break
return tuple(parts)

def _get_version(dependency_name):
try:
version_string: str = metadata.version(dependency_name)
parsed_version = parse_version_to_tuple(version_string)
return (parsed_version, version_string)
except Exception:
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
# or errors during parse_version_to_tuple
return (None, "--")

_dependency_package = "google.protobuf"
_next_supported_version = "4.25.8"
_next_supported_version_tuple = (4, 25, 8)
_recommendation = " (we recommend 6.x)"
(_version_used, _version_used_string) = _get_version(_dependency_package)
if _version_used and _version_used < _next_supported_version_tuple:
warnings.warn(
f"Package {_package_label} depends on "
+ f"{_dependency_package}, currently installed at version "
+ f"{_version_used_string}. Future updates to "
+ f"{_package_label} will require {_dependency_package} at "
+ f"version {_next_supported_version} or higher{_recommendation}."
+ " Please ensure "
+ "that either (a) your Python environment doesn't pin the "
+ f"version of {_dependency_package}, so that updates to "
+ f"{_package_label} can require the higher version, or "
+ "(b) you manually update your Python environment to use at "
+ f"least version {_next_supported_version} of "
+ f"{_dependency_package}.",
FutureWarning,
)
except Exception:
warnings.warn(
"Could not determine the version of Python "
+ "currently being used. To continue receiving "
+ "updates for {_package_label}, ensure you are "
+ "using a supported version of Python; see "
+ "https://devguide.python.org/versions/"
)
Comment on lines +176 to +183
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The general rules state that the library only supports Python versions 3.10 and greater. This except block handles cases where the Python version cannot be determined, but the warnings for older Python versions should ideally be removed if the library strictly enforces Python 3.10+.

References
  1. The library only supports Python versions 3.10 and greater.


__all__ = (
"StorageAsyncClient",
"AppendObjectSpec",
Expand Down
22 changes: 12 additions & 10 deletions google/cloud/_storage_v2/services/storage/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@

from google.cloud._storage_v2.services.storage import pagers
from google.cloud._storage_v2.types import storage
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
from google.longrunning import operations_pb2 # type: ignore
from google.protobuf import field_mask_pb2 # type: ignore
from google.protobuf import timestamp_pb2 # type: ignore
import google.iam.v1.iam_policy_pb2 as iam_policy_pb2 # type: ignore
import google.iam.v1.policy_pb2 as policy_pb2 # type: ignore
import google.protobuf.field_mask_pb2 as field_mask_pb2 # type: ignore
import google.protobuf.timestamp_pb2 as timestamp_pb2 # type: ignore
from .transports.base import StorageTransport, DEFAULT_CLIENT_INFO
from .transports.grpc_asyncio import StorageGrpcAsyncIOTransport
from .client import StorageClient
Expand Down Expand Up @@ -148,7 +148,8 @@ def from_service_account_info(cls, info: dict, *args, **kwargs):
Returns:
StorageAsyncClient: The constructed client.
"""
return StorageClient.from_service_account_info.__func__(StorageAsyncClient, info, *args, **kwargs) # type: ignore
sa_info_func = StorageClient.from_service_account_info.__func__ # type: ignore
return sa_info_func(StorageAsyncClient, info, *args, **kwargs)

@classmethod
def from_service_account_file(cls, filename: str, *args, **kwargs):
Expand All @@ -164,7 +165,8 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
Returns:
StorageAsyncClient: The constructed client.
"""
return StorageClient.from_service_account_file.__func__(StorageAsyncClient, filename, *args, **kwargs) # type: ignore
sa_file_func = StorageClient.from_service_account_file.__func__ # type: ignore
return sa_file_func(StorageAsyncClient, filename, *args, **kwargs)

from_service_account_json = from_service_account_file

Expand Down Expand Up @@ -214,7 +216,7 @@ def transport(self) -> StorageTransport:
return self._client.transport

@property
def api_endpoint(self):
def api_endpoint(self) -> str:
"""Return the API endpoint used by the client instance.

Returns:
Expand Down Expand Up @@ -1038,7 +1040,7 @@ async def get_iam_policy(
# client as shown in:
# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import storage_v2
from google.iam.v1 import iam_policy_pb2 # type: ignore
import google.iam.v1.iam_policy_pb2 as iam_policy_pb2 # type: ignore

async def sample_get_iam_policy():
# Create a client
Expand Down Expand Up @@ -1193,7 +1195,7 @@ async def set_iam_policy(
# client as shown in:
# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import storage_v2
from google.iam.v1 import iam_policy_pb2 # type: ignore
import google.iam.v1.iam_policy_pb2 as iam_policy_pb2 # type: ignore

async def sample_set_iam_policy():
# Create a client
Expand Down Expand Up @@ -1352,7 +1354,7 @@ async def test_iam_permissions(
# client as shown in:
# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import storage_v2
from google.iam.v1 import iam_policy_pb2 # type: ignore
import google.iam.v1.iam_policy_pb2 as iam_policy_pb2 # type: ignore

async def sample_test_iam_permissions():
# Create a client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
transport inheritance structure
_______________________________

`StorageTransport` is the ABC for all transports.
- public child `StorageGrpcTransport` for sync gRPC transport (defined in `grpc.py`).
- public child `StorageGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`).
- private child `_BaseStorageRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`).
- public child `StorageRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`).
``StorageTransport`` is the ABC for all transports.

- public child ``StorageGrpcTransport`` for sync gRPC transport (defined in ``grpc.py``).
- public child ``StorageGrpcAsyncIOTransport`` for async gRPC transport (defined in ``grpc_asyncio.py``).
- private child ``_BaseStorageRestTransport`` for base REST transport with inner classes ``_BaseMETHOD`` (defined in ``rest_base.py``).
- public child ``StorageRestTransport`` for sync REST transport with inner classes ``METHOD`` derived from the parent's corresponding ``_BaseMETHOD`` classes (defined in ``rest.py``).
23 changes: 16 additions & 7 deletions google/cloud/_storage_v2/services/storage/transports/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
import google.protobuf

from google.cloud._storage_v2.types import storage
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
from google.longrunning import operations_pb2 # type: ignore
from google.protobuf import empty_pb2 # type: ignore
import google.iam.v1.iam_policy_pb2 as iam_policy_pb2 # type: ignore
import google.iam.v1.policy_pb2 as policy_pb2 # type: ignore
import google.protobuf.empty_pb2 as empty_pb2 # type: ignore

DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
gapic_version=package_version.__version__
Expand Down Expand Up @@ -91,10 +91,12 @@ def __init__(
your own client library.
always_use_jwt_access (Optional[bool]): Whether self signed JWT should
be used for service account credentials.
api_audience (Optional[str]): The intended audience for the API calls
to the service that will be set when using certain 3rd party
authentication flows. Audience is typically a resource identifier.
If not set, the host value will be used as a default.
"""

scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES}

# Save the scopes.
self._scopes = scopes
if not hasattr(self, "_ignore_credentials"):
Expand All @@ -109,11 +111,16 @@ def __init__(

if credentials_file is not None:
credentials, _ = google.auth.load_credentials_from_file(
credentials_file, **scopes_kwargs, quota_project_id=quota_project_id
credentials_file,
scopes=scopes,
quota_project_id=quota_project_id,
default_scopes=self.AUTH_SCOPES,
)
elif credentials is None and not self._ignore_credentials:
credentials, _ = google.auth.default(
**scopes_kwargs, quota_project_id=quota_project_id
scopes=scopes,
quota_project_id=quota_project_id,
default_scopes=self.AUTH_SCOPES,
)
# Don't apply audience if the credentials file passed from user.
if hasattr(credentials, "with_gdch_audience"):
Expand All @@ -137,6 +144,8 @@ def __init__(
host += ":443"
self._host = host

self._wrapped_methods: Dict[Callable, Callable] = {}

@property
def host(self):
return self._host
Expand Down
14 changes: 9 additions & 5 deletions google/cloud/_storage_v2/services/storage/transports/grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
import proto # type: ignore

from google.cloud._storage_v2.types import storage
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
from google.longrunning import operations_pb2 # type: ignore
from google.protobuf import empty_pb2 # type: ignore
import google.iam.v1.iam_policy_pb2 as iam_policy_pb2 # type: ignore
import google.iam.v1.policy_pb2 as policy_pb2 # type: ignore
import google.protobuf.empty_pb2 as empty_pb2 # type: ignore
from .base import StorageTransport, DEFAULT_CLIENT_INFO

try:
Expand All @@ -59,7 +59,7 @@ def intercept_unary_unary(self, continuation, client_call_details, request):
elif isinstance(request, google.protobuf.message.Message):
request_payload = MessageToJson(request)
else:
request_payload = f"{type(request).__name__}: {pickle.dumps(request)}"
request_payload = f"{type(request).__name__}: {pickle.dumps(request)!r}"

request_metadata = {
key: value.decode("utf-8") if isinstance(value, bytes) else value
Expand Down Expand Up @@ -94,7 +94,7 @@ def intercept_unary_unary(self, continuation, client_call_details, request):
elif isinstance(result, google.protobuf.message.Message):
response_payload = MessageToJson(result)
else:
response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
response_payload = f"{type(result).__name__}: {pickle.dumps(result)!r}"
grpc_response = {
"payload": response_payload,
"metadata": metadata,
Expand Down Expand Up @@ -221,6 +221,10 @@ def __init__(
your own client library.
always_use_jwt_access (Optional[bool]): Whether self signed JWT should
be used for service account credentials.
api_audience (Optional[str]): The intended audience for the API calls
to the service that will be set when using certain 3rd party
authentication flows. Audience is typically a resource identifier.
If not set, the host value will be used as a default.

Raises:
google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
from grpc.experimental import aio # type: ignore

from google.cloud._storage_v2.types import storage
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
from google.longrunning import operations_pb2 # type: ignore
from google.protobuf import empty_pb2 # type: ignore
import google.iam.v1.iam_policy_pb2 as iam_policy_pb2 # type: ignore
import google.iam.v1.policy_pb2 as policy_pb2 # type: ignore
import google.protobuf.empty_pb2 as empty_pb2 # type: ignore
from .base import StorageTransport, DEFAULT_CLIENT_INFO
from .grpc import StorageGrpcTransport

Expand Down Expand Up @@ -65,7 +65,7 @@ async def intercept_unary_unary(self, continuation, client_call_details, request
elif isinstance(request, google.protobuf.message.Message):
request_payload = MessageToJson(request)
else:
request_payload = f"{type(request).__name__}: {pickle.dumps(request)}"
request_payload = f"{type(request).__name__}: {pickle.dumps(request)!r}"

request_metadata = {
key: value.decode("utf-8") if isinstance(value, bytes) else value
Expand Down Expand Up @@ -100,7 +100,7 @@ async def intercept_unary_unary(self, continuation, client_call_details, request
elif isinstance(result, google.protobuf.message.Message):
response_payload = MessageToJson(result)
else:
response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
response_payload = f"{type(result).__name__}: {pickle.dumps(result)!r}"
grpc_response = {
"payload": response_payload,
"metadata": metadata,
Expand Down Expand Up @@ -272,6 +272,10 @@ def __init__(
your own client library.
always_use_jwt_access (Optional[bool]): Whether self signed JWT should
be used for service account credentials.
api_audience (Optional[str]): The intended audience for the API calls
to the service that will be set when using certain 3rd party
authentication flows. Audience is typically a resource identifier.
If not set, the host value will be used as a default.

Raises:
google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
Expand Down
10 changes: 5 additions & 5 deletions google/cloud/_storage_v2/types/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@

import proto # type: ignore

from google.protobuf import duration_pb2 # type: ignore
from google.protobuf import field_mask_pb2 # type: ignore
from google.protobuf import timestamp_pb2 # type: ignore
from google.rpc import status_pb2 # type: ignore
from google.type import date_pb2 # type: ignore
import google.protobuf.duration_pb2 as duration_pb2 # type: ignore
import google.protobuf.field_mask_pb2 as field_mask_pb2 # type: ignore
import google.protobuf.timestamp_pb2 as timestamp_pb2 # type: ignore
import google.rpc.status_pb2 as status_pb2 # type: ignore
import google.type.date_pb2 as date_pb2 # type: ignore


__protobuf__ = proto.module(
Expand Down
14 changes: 13 additions & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
[mypy]
python_version = 3.7
python_version = 3.14
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The general rules state that the library only supports Python versions 3.10 and greater. Setting python_version = 3.14 in mypy.ini might lead to type-checking issues for Python 3.10-3.13. It's generally recommended to set this to the minimum supported Python version (3.10) to ensure compatibility across all supported versions, or to have separate configurations for each supported version.

References
  1. The library only supports Python versions 3.10 and greater.

namespace_packages = True
ignore_missing_imports = False

# TODO(https://github.com/googleapis/gapic-generator-python/issues/2563):
# Dependencies that historically lacks py.typed markers
[mypy-google.iam.*]
ignore_missing_imports = True

# Helps mypy navigate the 'google' namespace more reliably in 3.10+
explicit_package_bases = True

# Performance: reuse results from previous runs to speed up 'nox'
incremental = True
Loading
Loading