diff --git a/.flake8 b/.flake8 index 32986c792..90316de21 100644 --- a/.flake8 +++ b/.flake8 @@ -1,28 +1,29 @@ # -*- coding: utf-8 -*- -# -# Copyright 2024 Google LLC +# Copyright 2025 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 # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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. - -# Generated by synthtool. DO NOT EDIT! +# [flake8] +# TODO(https://github.com/googleapis/gapic-generator-python/issues/2333): +# Resolve flake8 lint issues ignore = E203, E231, E266, E501, W503 exclude = - # Exclude generated code. - **/proto/** + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2333): + # Ensure that generated code passes flake8 lint **/gapic/** **/services/** **/types/** + # Exclude Protobuf gencode *_pb2.py # Standard linting exemptions. diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index c631e1f7d..508ba98ef 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:5581906b957284864632cde4e9c51d1cc66b0094990b27e689132fe5cd036046 -# created: 2025-03-05 + digest: sha256:25de45b58e52021d3a24a6273964371a97a4efeefe6ad3845a64e697c63b6447 +# created: 2025-04-14T14:34:43.260858345Z diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index 3408b580a..ac5c87339 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -4,14 +4,14 @@ # Note: This file is autogenerated. To make changes to the assignee # team, please update `codeowner_team` in `.repo-metadata.json`. assign_issues: - - mukund-ananthu + - abbrowne126 assign_issues_by: - labels: - "samples" to: - googleapis/python-samples-reviewers - - mukund-ananthu + - abbrowne126 assign_prs: - - mukund-ananthu + - abbrowne126 diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 15777e3f6..db87e5c90 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.29.0" + ".": "2.29.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ffc0d132..1435f6752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.29.1](https://github.com/googleapis/python-pubsub/compare/v2.29.0...v2.29.1) (2025-05-23) + + +### Bug Fixes + +* Remove setup.cfg configuration for creating universal wheels ([#1376](https://github.com/googleapis/python-pubsub/issues/1376)) ([60639c4](https://github.com/googleapis/python-pubsub/commit/60639c4928105ae8a72c8e37b1f48f75cc2ffcc3)) + + +### Documentation + +* **sample:** Add samples for topic and subscription SMTs ([#1386](https://github.com/googleapis/python-pubsub/issues/1386)) ([4d072e0](https://github.com/googleapis/python-pubsub/commit/4d072e088b59f692dc3d59c3197a2993c125917e)) +* Update documentation for JavaScriptUDF to indicate that the `message_id` metadata field is optional instead of required ([#1380](https://github.com/googleapis/python-pubsub/issues/1380)) ([be90054](https://github.com/googleapis/python-pubsub/commit/be9005412fea06bea917c8b6861546b7e6c62a1e)) +* Update readme links ([#1409](https://github.com/googleapis/python-pubsub/issues/1409)) ([77ba05d](https://github.com/googleapis/python-pubsub/commit/77ba05d4ba5b84a25c1a07c5397bbc184fa6041d)) + ## [2.29.0](https://github.com/googleapis/python-pubsub/compare/v2.28.0...v2.29.0) (2025-03-19) diff --git a/MANIFEST.in b/MANIFEST.in index d6814cd60..dae249ec8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,25 +1,20 @@ # -*- coding: utf-8 -*- -# -# Copyright 2024 Google LLC +# Copyright 2025 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 # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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. - -# Generated by synthtool. DO NOT EDIT! +# include README.rst LICENSE -recursive-include google *.json *.proto py.typed +recursive-include google *.py *.pyi *.json *.proto py.typed recursive-include tests * global-exclude *.py[co] global-exclude __pycache__ - -# Exclude scripts for samples readmegen -prune scripts/readme-gen diff --git a/README.rst b/README.rst index dd3032e00..97010e998 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ independently written applications. :target: https://pypi.org/project/google-cloud-pubsub/ .. _Google Cloud Pub / Sub: https://cloud.google.com/pubsub/ .. _Product Documentation: https://cloud.google.com/pubsub/docs -.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/pubsub/latest +.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/summary_overview Quick Start ----------- @@ -116,7 +116,7 @@ messages to it To learn more, consult the `publishing documentation`_. -.. _publishing documentation: https://cloud.google.com/python/docs/reference/pubsub/latest +.. _publishing documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/google.cloud.pubsub_v1.publisher.client.Client Subscribing @@ -162,7 +162,7 @@ block the current thread until a given condition obtains: It is also possible to pull messages in a synchronous (blocking) fashion. To learn more about subscribing, consult the `subscriber documentation`_. -.. _subscriber documentation: https://cloud.google.com/python/docs/reference/pubsub/latest +.. _subscriber documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/google.cloud.pubsub_v1.subscriber.client.Client Authentication diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 07483fa04..f62042190 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.29.0" # {x-release-please-version} +__version__ = "2.29.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 07483fa04..f62042190 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.29.0" # {x-release-please-version} +__version__ = "2.29.1" # {x-release-please-version} diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 87838c006..52f1f1e30 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -37,6 +37,7 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: @@ -1662,5 +1663,8 @@ async def __aexit__(self, exc_type, exc, tb): client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + __all__ = ("PublisherAsyncClient",) diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index c94cec688..c8f39273e 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -47,6 +47,7 @@ from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] @@ -2129,5 +2130,7 @@ def test_iam_permissions( client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ __all__ = ("PublisherClient",) diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py index 354a6c714..0fb41c922 100644 --- a/google/pubsub_v1/services/publisher/transports/base.py +++ b/google/pubsub_v1/services/publisher/transports/base.py @@ -25,6 +25,7 @@ from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -35,6 +36,9 @@ client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class PublisherTransport(abc.ABC): """Abstract transport class for Publisher.""" diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index f27b968a8..9e8ed1737 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -73,12 +73,11 @@ def intercept_unary_unary(self, continuation, client_call_details, request): f"Sending request for {client_call_details.method}", extra={ "serviceName": "google.pubsub.v1.Publisher", - "rpcName": client_call_details.method, + "rpcName": str(client_call_details.method), "request": grpc_request, "metadata": grpc_request["metadata"], }, ) - response = continuation(client_call_details, request) if logging_enabled: # pragma: NO COVER response_metadata = response.trailing_metadata() diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index a5c99da1b..92ee46e15 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -23,6 +23,7 @@ from google.api_core import rest_helpers from google.api_core import rest_streaming from google.api_core import gapic_v1 +import google.protobuf from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -61,6 +62,9 @@ rest_version=f"requests@{requests_version}", ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class PublisherRestInterceptor: """Interceptor for Publisher. diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index 4ebf3ac94..b2d139fa0 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -37,6 +37,7 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: @@ -1775,5 +1776,8 @@ async def __aexit__(self, exc_type, exc, tb): client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + __all__ = ("SchemaServiceAsyncClient",) diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index 40fc381b8..493ffd2b6 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -46,6 +46,7 @@ from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] @@ -2190,5 +2191,7 @@ def test_iam_permissions( client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ __all__ = ("SchemaServiceClient",) diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py index c1187d1cd..c30773b74 100644 --- a/google/pubsub_v1/services/schema_service/transports/base.py +++ b/google/pubsub_v1/services/schema_service/transports/base.py @@ -25,6 +25,7 @@ from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -36,6 +37,9 @@ client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class SchemaServiceTransport(abc.ABC): """Abstract transport class for SchemaService.""" diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index f089c2724..1a746ef43 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -74,12 +74,11 @@ def intercept_unary_unary(self, continuation, client_call_details, request): f"Sending request for {client_call_details.method}", extra={ "serviceName": "google.pubsub.v1.SchemaService", - "rpcName": client_call_details.method, + "rpcName": str(client_call_details.method), "request": grpc_request, "metadata": grpc_request["metadata"], }, ) - response = continuation(client_call_details, request) if logging_enabled: # pragma: NO COVER response_metadata = response.trailing_metadata() diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index f4eab1dd8..6d8902c5d 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -23,6 +23,7 @@ from google.api_core import rest_helpers from google.api_core import rest_streaming from google.api_core import gapic_v1 +import google.protobuf from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -62,6 +63,9 @@ rest_version=f"requests@{requests_version}", ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class SchemaServiceRestInterceptor: """Interceptor for SchemaService. diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 4ad7306f2..5d3dd3da7 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -41,6 +41,7 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: @@ -2635,5 +2636,8 @@ async def __aexit__(self, exc_type, exc, tb): client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + __all__ = ("SubscriberAsyncClient",) diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index 44f3b1598..eef3ab77d 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -49,6 +49,7 @@ from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] @@ -3087,5 +3088,7 @@ def test_iam_permissions( client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ __all__ = ("SubscriberClient",) diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py index 01fd7ddcb..cc62113f9 100644 --- a/google/pubsub_v1/services/subscriber/transports/base.py +++ b/google/pubsub_v1/services/subscriber/transports/base.py @@ -25,6 +25,7 @@ from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -35,6 +36,9 @@ client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class SubscriberTransport(abc.ABC): """Abstract transport class for Subscriber.""" diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index 6a10424fe..ec400da0c 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -73,12 +73,11 @@ def intercept_unary_unary(self, continuation, client_call_details, request): f"Sending request for {client_call_details.method}", extra={ "serviceName": "google.pubsub.v1.Subscriber", - "rpcName": client_call_details.method, + "rpcName": str(client_call_details.method), "request": grpc_request, "metadata": grpc_request["metadata"], }, ) - response = continuation(client_call_details, request) if logging_enabled: # pragma: NO COVER response_metadata = response.trailing_metadata() diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index 62211228e..bb0ee68c6 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -23,6 +23,7 @@ from google.api_core import rest_helpers from google.api_core import rest_streaming from google.api_core import gapic_v1 +import google.protobuf from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -61,6 +62,9 @@ rest_version=f"requests@{requests_version}", ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class SubscriberRestInterceptor: """Interceptor for Subscriber. diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 74b65549b..6f27edfee 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1119,7 +1119,7 @@ class JavaScriptUDF(proto.Message): * * @param {Object} metadata - Pub/Sub message metadata. * Keys: - * - (required) 'message_id' : {string} + * - (optional) 'message_id' : {string} * - (optional) 'publish_time': {string} YYYY-MM-DDTHH:MM:SSZ format * - (optional) 'ordering_key': {string} */ diff --git a/owlbot.py b/owlbot.py index a8044bd6f..d845e5758 100644 --- a/owlbot.py +++ b/owlbot.py @@ -326,7 +326,7 @@ if count < 1: raise Exception(".coveragerc replacement failed.") - s.move([library], excludes=["**/gapic_version.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) + s.move([library], excludes=["**/gapic_version.py", "noxfile.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) s.remove_staging_dirs() # ---------------------------------------------------------------------------- @@ -343,99 +343,7 @@ system_test_python_versions=["3.12"], system_test_external_dependencies=["psutil","flaky"], ) -s.move(templated_files, excludes=[".coveragerc", ".github/release-please.yml", "README.rst", "docs/index.rst"]) - -# ---------------------------------------------------------------------------- -# Add mypy nox session. -# ---------------------------------------------------------------------------- -s.replace( - "noxfile.py", - r"LINT_PATHS = \[.*?\]", - '\g<0>\n\nMYPY_VERSION = "mypy==1.10.0"', -) -s.replace( - "noxfile.py", r'"blacken",', '\g<0>\n "mypy",', -) -s.replace( - "noxfile.py", - r"nox\.options\.error_on_missing_interpreters = True", - textwrap.dedent( - ''' \g<0> - - - @nox.session(python=DEFAULT_PYTHON_VERSION) - def mypy(session): - """Run type checks with mypy.""" - session.install("-e", ".[all]") - session.install(MYPY_VERSION) - - # Version 2.1.1 of google-api-core version is the first type-checked release. - # Version 2.2.0 of google-cloud-core version is the first type-checked release. - session.install( - "google-api-core[grpc]>=2.1.1", - "google-cloud-core>=2.2.0", - ) - - # Just install the type info directly, since "mypy --install-types" might - # require an additional pass. - # Exclude types-protobuf==4.24.0.20240106 - # See https://github.com/python/typeshed/issues/11254 - session.install("types-protobuf!=4.24.0.20240106", "types-setuptools") - - # TODO: Only check the hand-written layer, the generated code does not pass - # mypy checks yet. - # https://github.com/googleapis/gapic-generator-python/issues/1092 - session.run("mypy", "-p", "google.cloud")''' - ), -) - - -# ---------------------------------------------------------------------------- -# Add mypy_samples nox session. -# ---------------------------------------------------------------------------- -s.replace( - "noxfile.py", - r' "mypy",', - '\g<0>\n # https://github.com/googleapis/python-pubsub/pull/552#issuecomment-1016256936' - '\n # "mypy_samples", # TODO: uncomment when the check passes', -) -s.replace( - "noxfile.py", - r'session\.run\("mypy", "-p", "google.cloud"\)', - textwrap.dedent( - ''' \g<0> - - - @nox.session(python=DEFAULT_PYTHON_VERSION) - def mypy_samples(session): - """Run type checks with mypy.""" - - session.install("-e", ".[all]") - - session.install("pytest") - session.install(MYPY_VERSION) - - # Just install the type info directly, since "mypy --install-types" might - # require an additional pass. - session.install("types-mock", "types-protobuf", "types-setuptools") - - session.run( - "mypy", - "--config-file", - str(CURRENT_DIRECTORY / "samples" / "snippets" / "mypy.ini"), - "--no-incremental", # Required by warn-unused-configs from mypy.ini to work - "samples/", - )''' - ), -) - - -# Only consider the hand-written layer when assessing the test coverage. -s.replace( - "noxfile.py", "--cov=google", "--cov=google/cloud", -) - -s.replace(".github/blunderbuss.yml", "googleapis/api-pubsub", "mukund-ananthu") +s.move(templated_files, excludes=[".coveragerc", ".github/blunderbuss.yml", ".github/release-please.yml", "README.rst", "docs/index.rst"]) python.py_samples(skip_readmes=True) diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 9f802d89d..dbb835bb3 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.29.0" + "version": "2.29.1" }, "snippets": [ { diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index e279324b8..d2b6dd2b8 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -326,6 +326,39 @@ def create_topic_with_confluent_cloud_ingestion( # [END pubsub_create_topic_with_confluent_cloud_ingestion] +def create_topic_with_smt( + project_id: str, + topic_id: str, +) -> None: + """Create a new Pub/Sub topic with a UDF SMT.""" + # [START pubsub_create_topic_with_smt] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import JavaScriptUDF, MessageTransform, Topic + + # TODO(developer) + # project_id = "your-project-id" + # topic_id = "your-topic-id" + + code = """function redactSSN(message, metadata) { + const data = JSON.parse(message.data); + delete data['ssn']; + message.data = JSON.stringify(data); + return message; + }""" + udf = JavaScriptUDF(code=code, function_name="redactSSN") + transforms = [MessageTransform(javascript_udf=udf)] + + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + + request = Topic(name=topic_path, message_transforms=transforms) + + topic = publisher.create_topic(request=request) + + print(f"Created topic: {topic.name} with SMT") + # [END pubsub_create_topic_with_smt] + + def update_topic_type( project_id: str, topic_id: str, @@ -888,6 +921,11 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: "gcp_service_account" ) + create_parser = subparsers.add_parser( + "create_smt", help=create_topic_with_smt.__doc__ + ) + create_parser.add_argument("topic_id") + update_topic_type_parser = subparsers.add_parser( "update_kinesis_ingestion", help=update_topic_type.__doc__ ) @@ -1007,6 +1045,11 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: args.identity_pool_id, args.gcp_service_account, ) + elif args.command == "create_smt": + create_topic_with_smt( + args.project_id, + args.topic_id, + ) elif args.command == "update_kinesis_ingestion": update_topic_type( args.project_id, diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py index dc7b94027..1c691bd5c 100644 --- a/samples/snippets/publisher_test.py +++ b/samples/snippets/publisher_test.py @@ -313,6 +313,26 @@ def test_create_topic_with_confluent_cloud_ingestion( publisher_client.delete_topic(request={"topic": topic_path}) +def test_create_with_smt( + publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] +) -> None: + # The scope of `topic_path` is limited to this function. + topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + + try: + publisher_client.delete_topic(request={"topic": topic_path}) + except NotFound: + pass + + publisher.create_topic_with_smt(PROJECT_ID, TOPIC_ID) + + out, _ = capsys.readouterr() + assert f"Created topic: {topic_path} with SMT" in out + + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + + def test_update_topic_type( publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] ) -> None: diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index aa57a68a2..2bf14b760 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,7 +1,8 @@ backoff==2.2.1 pytest===7.4.4; python_version == '3.7' -pytest==8.3.4; python_version >= '3.8' -mock==5.1.0 +pytest==8.3.5; python_version >= '3.8' +mock==5.2.0 flaky==3.8.1 -google-cloud-bigquery==3.27.0 -google-cloud-storage==2.19.0 +google-cloud-bigquery==3.30.0; python_version < '3.9' +google-cloud-bigquery==3.33.0; python_version >= '3.9' +google-cloud-storage==3.1.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index b6ae767c6..83202d4f8 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,10 +1,11 @@ -google-cloud-pubsub==2.28.0 +google-cloud-pubsub==2.29.0 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' -protobuf==5.29.2; python_version >= '3.8' +protobuf===5.29.4; python_version == '3.8' +protobuf==6.31.0; python_version >= '3.9' avro==1.12.0 opentelemetry-api===1.22.0; python_version == '3.7' opentelemetry-sdk===1.22.0; python_version == '3.7' -opentelemetry-api==1.29.0; python_version >= '3.8' -opentelemetry-sdk==1.29.0; python_version >= '3.8' -opentelemetry-exporter-gcp-trace==1.7.0 +opentelemetry-api==1.33.1; python_version >= '3.8' +opentelemetry-sdk==1.33.1; python_version >= '3.8' +opentelemetry-exporter-gcp-trace==1.9.0 diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py index c09f5def1..5549d056f 100644 --- a/samples/snippets/subscriber.py +++ b/samples/snippets/subscriber.py @@ -578,6 +578,45 @@ def create_cloudstorage_subscription( # [END pubsub_create_cloud_storage_subscription] +def create_subscription_with_smt( + project_id: str, topic_id: str, subscription_id: str +) -> None: + """Create a subscription with a UDF SMT.""" + # [START pubsub_create_subscription_with_smt] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import JavaScriptUDF, MessageTransform + + # TODO(developer): Choose an existing topic. + # project_id = "your-project-id" + # topic_id = "your-topic-id" + # subscription_id = "your-subscription-id" + + publisher = pubsub_v1.PublisherClient() + subscriber = pubsub_v1.SubscriberClient() + topic_path = publisher.topic_path(project_id, topic_id) + subscription_path = subscriber.subscription_path(project_id, subscription_id) + + code = """function redactSSN(message, metadata) { + const data = JSON.parse(message.data); + delete data['ssn']; + message.data = JSON.stringify(data); + return message; + }""" + udf = JavaScriptUDF(code=code, function_name="redactSSN") + transforms = [MessageTransform(javascript_udf=udf)] + + with subscriber: + subscription = subscriber.create_subscription( + request={ + "name": subscription_path, + "topic": topic_path, + "message_transforms": transforms, + } + ) + print(f"Created subscription with SMT: {subscription}") + # [END pubsub_create_subscription_with_smt] + + def delete_subscription(project_id: str, subscription_id: str) -> None: """Deletes an existing Pub/Sub topic.""" # [START pubsub_delete_subscription] @@ -1310,6 +1349,12 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: create_cloudstorage_subscription_parser.add_argument("subscription_id") create_cloudstorage_subscription_parser.add_argument("bucket") + create_subscription_with_smt_parser = subparsers.add_parser( + "create-with-smt", help=create_subscription_with_smt.__doc__ + ) + create_subscription_with_smt_parser.add_argument("topic_id") + create_subscription_with_smt_parser.add_argument("subscription_id") + delete_parser = subparsers.add_parser("delete", help=delete_subscription.__doc__) delete_parser.add_argument("subscription_id") @@ -1471,6 +1516,10 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: create_cloudstorage_subscription( args.project_id, args.topic_id, args.subscription_id, args.bucket ) + elif args.command == "create-with-smt": + create_subscription_with_smt( + args.project_id, args.topic_id, args.subscription_id + ) elif args.command == "delete": delete_subscription(args.project_id, args.subscription_id) diff --git a/samples/snippets/subscriber_test.py b/samples/snippets/subscriber_test.py index 86f7a94ce..53a844e01 100644 --- a/samples/snippets/subscriber_test.py +++ b/samples/snippets/subscriber_test.py @@ -579,6 +579,37 @@ def test_create_push_subscription( subscriber_client.delete_subscription(request={"subscription": subscription_path}) +def test_create_subscription_with_smt( + subscriber_client: pubsub_v1.SubscriberClient, + topic: str, + capsys: CaptureFixture[str], +) -> None: + subscription_for_create_name = ( + f"subscription-test-subscription-for-create-with-smt-{PY_VERSION}-{UUID}" + ) + + subscription_path = subscriber_client.subscription_path( + PROJECT_ID, subscription_for_create_name + ) + + try: + subscriber_client.delete_subscription( + request={"subscription": subscription_path} + ) + except NotFound: + pass + + subscriber.create_subscription_with_smt( + PROJECT_ID, TOPIC, subscription_for_create_name + ) + + out, _ = capsys.readouterr() + assert f"{subscription_for_create_name}" in out + + # Clean up. + subscriber_client.delete_subscription(request={"subscription": subscription_path}) + + def test_update_push_subscription( subscriber_client: pubsub_v1.SubscriberClient, topic: str, diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 052350089..000000000 --- a/setup.cfg +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2023 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 -# -# https://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. - -# Generated by synthtool. DO NOT EDIT! -[bdist_wheel] -universal = 1 diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt index ad3f0fa58..2010e549c 100644 --- a/testing/constraints-3.13.txt +++ b/testing/constraints-3.13.txt @@ -1,7 +1,12 @@ -# -*- coding: utf-8 -*- -# This constraints file is required for unit tests. +# We use the constraints file for the latest Python version +# (currently this file) to check that the latest +# major versions of dependencies are supported in setup.py. # List all library dependencies and extras in this file. -google-api-core -proto-plus -protobuf -grpc-google-iam-v1 +# Require the latest major version be installed for each dependency. +# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0", +# Then this file should have google-cloud-foo>=1 +google-api-core>=2 +google-auth>=2 +proto-plus>=1 +protobuf>=6 +grpc-google-iam-v1>=0