From 448923acf277a70e8704c949311bf4feaef8cab6 Mon Sep 17 00:00:00 2001 From: Charly Laurent Date: Fri, 17 Nov 2023 19:22:22 +0100 Subject: [PATCH 01/11] fix: replace deprecated `datetime.datetime.utcnow()` (#552) Fixes #540 Co-authored-by: Anthonios Partheniou --- google/api_core/datetime_helpers.py | 2 +- noxfile.py | 2 +- testing/constraints-3.12.txt | 0 tests/asyncio/test_retry_async.py | 4 +--- tests/unit/test_retry.py | 4 +--- tests/unit/test_timeout.py | 8 ++++---- 6 files changed, 8 insertions(+), 12 deletions(-) create mode 100644 testing/constraints-3.12.txt diff --git a/google/api_core/datetime_helpers.py b/google/api_core/datetime_helpers.py index c584c8cd..c3792300 100644 --- a/google/api_core/datetime_helpers.py +++ b/google/api_core/datetime_helpers.py @@ -42,7 +42,7 @@ def utcnow(): """A :meth:`datetime.datetime.utcnow()` alias to allow mocking in tests.""" - return datetime.datetime.utcnow() + return datetime.datetime.now(tz=datetime.timezone.utc).replace(tzinfo=None) def to_milliseconds(value): diff --git a/noxfile.py b/noxfile.py index 82ea2669..d0ce6d6c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -143,7 +143,7 @@ def default(session, install_grpc=True): session.run(*pytest_args) -@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11"]) +@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]) def unit(session): """Run the unit test suite.""" default(session) diff --git a/testing/constraints-3.12.txt b/testing/constraints-3.12.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/asyncio/test_retry_async.py b/tests/asyncio/test_retry_async.py index 14807eb5..16f5c3db 100644 --- a/tests/asyncio/test_retry_async.py +++ b/tests/asyncio/test_retry_async.py @@ -280,7 +280,6 @@ async def test___call___and_execute_success(self, sleep): @mock.patch("asyncio.sleep", autospec=True) @pytest.mark.asyncio async def test___call___and_execute_retry(self, sleep, uniform): - on_error = mock.Mock(spec=["__call__"], side_effect=[None]) retry_ = retry_async.AsyncRetry( predicate=retry_async.if_exception_type(ValueError) @@ -305,7 +304,6 @@ async def test___call___and_execute_retry(self, sleep, uniform): @mock.patch("asyncio.sleep", autospec=True) @pytest.mark.asyncio async def test___call___and_execute_retry_hitting_deadline(self, sleep, uniform): - on_error = mock.Mock(spec=["__call__"], side_effect=[None] * 10) retry_ = retry_async.AsyncRetry( predicate=retry_async.if_exception_type(ValueError), @@ -315,7 +313,7 @@ async def test___call___and_execute_retry_hitting_deadline(self, sleep, uniform) deadline=9.9, ) - utcnow = datetime.datetime.utcnow() + utcnow = datetime.datetime.now(tz=datetime.timezone.utc) utcnow_patcher = mock.patch( "google.api_core.datetime_helpers.utcnow", return_value=utcnow ) diff --git a/tests/unit/test_retry.py b/tests/unit/test_retry.py index d770f7a2..2faf77c9 100644 --- a/tests/unit/test_retry.py +++ b/tests/unit/test_retry.py @@ -361,7 +361,6 @@ def test___call___and_execute_success(self, sleep): @mock.patch("random.uniform", autospec=True, side_effect=lambda m, n: n) @mock.patch("time.sleep", autospec=True) def test___call___and_execute_retry(self, sleep, uniform): - on_error = mock.Mock(spec=["__call__"], side_effect=[None]) retry_ = retry.Retry(predicate=retry.if_exception_type(ValueError)) @@ -383,7 +382,6 @@ def test___call___and_execute_retry(self, sleep, uniform): @mock.patch("random.uniform", autospec=True, side_effect=lambda m, n: n) @mock.patch("time.sleep", autospec=True) def test___call___and_execute_retry_hitting_deadline(self, sleep, uniform): - on_error = mock.Mock(spec=["__call__"], side_effect=[None] * 10) retry_ = retry.Retry( predicate=retry.if_exception_type(ValueError), @@ -393,7 +391,7 @@ def test___call___and_execute_retry_hitting_deadline(self, sleep, uniform): deadline=30.9, ) - utcnow = datetime.datetime.utcnow() + utcnow = datetime.datetime.now(tz=datetime.timezone.utc) utcnow_patcher = mock.patch( "google.api_core.datetime_helpers.utcnow", return_value=utcnow ) diff --git a/tests/unit/test_timeout.py b/tests/unit/test_timeout.py index a83a2ecb..0bcf07f0 100644 --- a/tests/unit/test_timeout.py +++ b/tests/unit/test_timeout.py @@ -58,10 +58,10 @@ def test___str__(self): def test_apply(self): target = mock.Mock(spec=["__call__", "__name__"], __name__="target") - datetime.datetime.utcnow() + datetime.datetime.now(tz=datetime.timezone.utc) datetime.timedelta(seconds=1) - now = datetime.datetime.utcnow() + now = datetime.datetime.now(tz=datetime.timezone.utc) times = [ now, @@ -92,10 +92,10 @@ def _clock(): def test_apply_no_timeout(self): target = mock.Mock(spec=["__call__", "__name__"], __name__="target") - datetime.datetime.utcnow() + datetime.datetime.now(tz=datetime.timezone.utc) datetime.timedelta(seconds=1) - now = datetime.datetime.utcnow() + now = datetime.datetime.now(tz=datetime.timezone.utc) times = [ now, From fc12b40bfc6e0c4bb313196e2e3a9c9374ce1c45 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Nov 2023 10:29:55 -0800 Subject: [PATCH 02/11] feat: add type annotations to wrapped grpc calls (#554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add types to grpc call wrappers * fixed tests * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * changed type * changed async types * added tests * fixed lint issues * Update tests/asyncio/test_grpc_helpers_async.py Co-authored-by: Anthonios Partheniou * turned GrpcStream into a type alias * added test for GrpcStream * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * added comment * reordered types * changed type var to P --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- google/api_core/grpc_helpers.py | 14 ++++++++--- google/api_core/grpc_helpers_async.py | 30 ++++++++++++++++-------- tests/asyncio/test_grpc_helpers_async.py | 22 +++++++++++++++++ tests/unit/test_grpc_helpers.py | 17 ++++++++++++++ 4 files changed, 70 insertions(+), 13 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index f52e180a..793c884d 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -13,6 +13,7 @@ # limitations under the License. """Helpers for :mod:`grpc`.""" +from typing import Generic, TypeVar, Iterator import collections import functools @@ -54,6 +55,9 @@ _LOGGER = logging.getLogger(__name__) +# denotes the proto response type for grpc calls +P = TypeVar("P") + def _patch_callable_name(callable_): """Fix-up gRPC callable attributes. @@ -79,7 +83,7 @@ def error_remapped_callable(*args, **kwargs): return error_remapped_callable -class _StreamingResponseIterator(grpc.Call): +class _StreamingResponseIterator(Generic[P], grpc.Call): def __init__(self, wrapped, prefetch_first_result=True): self._wrapped = wrapped @@ -97,11 +101,11 @@ def __init__(self, wrapped, prefetch_first_result=True): # ignore stop iteration at this time. This should be handled outside of retry. pass - def __iter__(self): + def __iter__(self) -> Iterator[P]: """This iterator is also an iterable that returns itself.""" return self - def __next__(self): + def __next__(self) -> P: """Get the next response from the stream. Returns: @@ -144,6 +148,10 @@ def trailing_metadata(self): return self._wrapped.trailing_metadata() +# public type alias denoting the return type of streaming gapic calls +GrpcStream = _StreamingResponseIterator[P] + + def _wrap_stream_errors(callable_): """Wrap errors for Unary-Stream and Stream-Stream gRPC callables. diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index d1f69d98..5685e6f8 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -21,11 +21,15 @@ import asyncio import functools +from typing import Generic, Iterator, AsyncGenerator, TypeVar + import grpc from grpc import aio from google.api_core import exceptions, grpc_helpers +# denotes the proto response type for grpc calls +P = TypeVar("P") # NOTE(lidiz) Alternatively, we can hack "__getattribute__" to perform # automatic patching for us. But that means the overhead of creating an @@ -75,8 +79,8 @@ async def wait_for_connection(self): raise exceptions.from_grpc_error(rpc_error) from rpc_error -class _WrappedUnaryResponseMixin(_WrappedCall): - def __await__(self): +class _WrappedUnaryResponseMixin(Generic[P], _WrappedCall): + def __await__(self) -> Iterator[P]: try: response = yield from self._call.__await__() return response @@ -84,17 +88,17 @@ def __await__(self): raise exceptions.from_grpc_error(rpc_error) from rpc_error -class _WrappedStreamResponseMixin(_WrappedCall): +class _WrappedStreamResponseMixin(Generic[P], _WrappedCall): def __init__(self): self._wrapped_async_generator = None - async def read(self): + async def read(self) -> P: try: return await self._call.read() except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error - async def _wrapped_aiter(self): + async def _wrapped_aiter(self) -> AsyncGenerator[P, None]: try: # NOTE(lidiz) coverage doesn't understand the exception raised from # __anext__ method. It is covered by test case: @@ -104,7 +108,7 @@ async def _wrapped_aiter(self): except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error - def __aiter__(self): + def __aiter__(self) -> AsyncGenerator[P, None]: if not self._wrapped_async_generator: self._wrapped_async_generator = self._wrapped_aiter() return self._wrapped_async_generator @@ -127,26 +131,32 @@ async def done_writing(self): # NOTE(lidiz) Implementing each individual class separately, so we don't # expose any API that should not be seen. E.g., __aiter__ in unary-unary # RPC, or __await__ in stream-stream RPC. -class _WrappedUnaryUnaryCall(_WrappedUnaryResponseMixin, aio.UnaryUnaryCall): +class _WrappedUnaryUnaryCall(_WrappedUnaryResponseMixin[P], aio.UnaryUnaryCall): """Wrapped UnaryUnaryCall to map exceptions.""" -class _WrappedUnaryStreamCall(_WrappedStreamResponseMixin, aio.UnaryStreamCall): +class _WrappedUnaryStreamCall(_WrappedStreamResponseMixin[P], aio.UnaryStreamCall): """Wrapped UnaryStreamCall to map exceptions.""" class _WrappedStreamUnaryCall( - _WrappedUnaryResponseMixin, _WrappedStreamRequestMixin, aio.StreamUnaryCall + _WrappedUnaryResponseMixin[P], _WrappedStreamRequestMixin, aio.StreamUnaryCall ): """Wrapped StreamUnaryCall to map exceptions.""" class _WrappedStreamStreamCall( - _WrappedStreamRequestMixin, _WrappedStreamResponseMixin, aio.StreamStreamCall + _WrappedStreamRequestMixin, _WrappedStreamResponseMixin[P], aio.StreamStreamCall ): """Wrapped StreamStreamCall to map exceptions.""" +# public type alias denoting the return type of async streaming gapic calls +GrpcAsyncStream = _WrappedStreamResponseMixin[P] +# public type alias denoting the return type of unary gapic calls +AwaitableGrpcCall = _WrappedUnaryResponseMixin[P] + + def _wrap_unary_errors(callable_): """Map errors for Unary-Unary async callables.""" grpc_helpers._patch_callable_name(callable_) diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py index 95242f6b..67c9b335 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -266,6 +266,28 @@ def test_wrap_errors_non_streaming(wrap_unary_errors): wrap_unary_errors.assert_called_once_with(callable_) +def test_grpc_async_stream(): + """ + GrpcAsyncStream type should be both an AsyncIterator and a grpc.aio.Call. + """ + instance = grpc_helpers_async.GrpcAsyncStream[int]() + assert isinstance(instance, grpc.aio.Call) + # should implement __aiter__ and __anext__ + assert hasattr(instance, "__aiter__") + it = instance.__aiter__() + assert hasattr(it, "__anext__") + + +def test_awaitable_grpc_call(): + """ + AwaitableGrpcCall type should be an Awaitable and a grpc.aio.Call. + """ + instance = grpc_helpers_async.AwaitableGrpcCall[int]() + assert isinstance(instance, grpc.aio.Call) + # should implement __await__ + assert hasattr(instance, "__await__") + + @mock.patch("google.api_core.grpc_helpers_async._wrap_stream_errors") def test_wrap_errors_streaming(wrap_stream_errors): callable_ = mock.create_autospec(aio.UnaryStreamMultiCallable) diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index 4eccbcaa..58a6a329 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -195,6 +195,23 @@ def test_trailing_metadata(self): wrapped.trailing_metadata.assert_called_once_with() +class TestGrpcStream(Test_StreamingResponseIterator): + @staticmethod + def _make_one(wrapped, **kw): + return grpc_helpers.GrpcStream(wrapped, **kw) + + def test_grpc_stream_attributes(self): + """ + Should be both a grpc.Call and an iterable + """ + call = self._make_one(None) + assert isinstance(call, grpc.Call) + # should implement __iter__ + assert hasattr(call, "__iter__") + it = call.__iter__() + assert hasattr(it, "__next__") + + def test_wrap_stream_okay(): expected_responses = [1, 2, 3] callable_ = mock.Mock(spec=["__call__"], return_value=iter(expected_responses)) From 3069ef4b9123ddb64841cbb7bbb183b53d502e0a Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 20 Nov 2023 14:19:31 -0500 Subject: [PATCH 03/11] feat: add universe_domain attribute to ClientOptions (#546) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add universe_domain option for TPC * test: update options to include universe domain * update the docstring * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Update google/api_core/client_options.py Co-authored-by: Anthonios Partheniou * Update client_options.py --------- Co-authored-by: omair Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- google/api_core/client_options.py | 7 +++++++ tests/unit/test_client_options.py | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/google/api_core/client_options.py b/google/api_core/client_options.py index ee9f28a9..e93f9586 100644 --- a/google/api_core/client_options.py +++ b/google/api_core/client_options.py @@ -75,6 +75,11 @@ class ClientOptions(object): authentication flows. Audience is typically a resource identifier. If not set, the service endpoint value will be used as a default. An example of a valid ``api_audience`` is: "https://language.googleapis.com". + universe_domain (Optional[str]): The desired universe domain. This must match + the one in credentials. If not set, the default universe domain is + `googleapis.com`. If both `api_endpoint` and `universe_domain` are set, + then `api_endpoint` is used as the service endpoint. If `api_endpoint` is + not specified, the format will be `{service}.{universe_domain}`. Raises: ValueError: If both ``client_cert_source`` and ``client_encrypted_cert_source`` @@ -91,6 +96,7 @@ def __init__( scopes=None, api_key=None, api_audience=None, + universe_domain=None, ): if client_cert_source and client_encrypted_cert_source: raise ValueError( @@ -106,6 +112,7 @@ def __init__( self.scopes = scopes self.api_key = api_key self.api_audience = api_audience + self.universe_domain = universe_domain def __repr__(self): return "ClientOptions: " + repr(self.__dict__) diff --git a/tests/unit/test_client_options.py b/tests/unit/test_client_options.py index 336ceeab..396d6627 100644 --- a/tests/unit/test_client_options.py +++ b/tests/unit/test_client_options.py @@ -38,6 +38,7 @@ def test_constructor(): "https://www.googleapis.com/auth/cloud-platform.read-only", ], api_audience="foo2.googleapis.com", + universe_domain="googleapis.com", ) assert options.api_endpoint == "foo.googleapis.com" @@ -49,6 +50,7 @@ def test_constructor(): "https://www.googleapis.com/auth/cloud-platform.read-only", ] assert options.api_audience == "foo2.googleapis.com" + assert options.universe_domain == "googleapis.com" def test_constructor_with_encrypted_cert_source(): @@ -110,6 +112,7 @@ def test_from_dict(): options = client_options.from_dict( { "api_endpoint": "foo.googleapis.com", + "universe_domain": "googleapis.com", "client_cert_source": get_client_cert, "quota_project_id": "quote-proj", "credentials_file": "path/to/credentials.json", @@ -122,6 +125,7 @@ def test_from_dict(): ) assert options.api_endpoint == "foo.googleapis.com" + assert options.universe_domain == "googleapis.com" assert options.client_cert_source() == (b"cert", b"key") assert options.quota_project_id == "quote-proj" assert options.credentials_file == "path/to/credentials.json" @@ -148,6 +152,7 @@ def test_repr(): expected_keys = set( [ "api_endpoint", + "universe_domain", "client_cert_source", "client_encrypted_cert_source", "quota_project_id", From 40c8ae0cf1f797e31e106461164e22db4fb2d3d9 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Fri, 1 Dec 2023 10:51:41 -0500 Subject: [PATCH 04/11] fix: fix regression in `bidi` causing `Thread-ConsumeBidirectionalStream caught unexpected exception and will exit` (#562) * chore: partial revert of PR #357 This reverts commit e120a0cdb589d390848b0711ff21c9ff4aab26a9. * add comment --- google/api_core/bidi.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/google/api_core/bidi.py b/google/api_core/bidi.py index 74abc495..17d836d5 100644 --- a/google/api_core/bidi.py +++ b/google/api_core/bidi.py @@ -91,8 +91,12 @@ def __init__(self, queue, period=1, initial_request=None): def _is_active(self): # Note: there is a possibility that this starts *before* the call # property is set. So we have to check if self.call is set before - # seeing if it's active. - return self.call is not None and self.call.is_active() + # seeing if it's active. We need to return True if self.call is None. + # See https://github.com/googleapis/python-api-core/issues/560. + if self.call is not None and not self.call.is_active(): + return False + else: + return True def __iter__(self): if self._initial_request is not None: From bd82827108f1eeb6c05cfacf6c044b2afacc18a2 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Fri, 1 Dec 2023 10:53:21 -0500 Subject: [PATCH 05/11] feat: Introduce compatibility with native namespace packages (#561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Introduce compatibility with native namespace packages * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- google/__init__.py | 25 ------------------------- setup.py | 11 +++-------- tests/unit/test_packaging.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 33 deletions(-) delete mode 100644 google/__init__.py create mode 100644 tests/unit/test_packaging.py diff --git a/google/__init__.py b/google/__init__.py deleted file mode 100644 index 9f1d5491..00000000 --- a/google/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2016 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 -# -# 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. - -"""Google namespace package.""" - -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - # See: https://github.com/python/mypy/issues/1422 - __path__ = pkgutil.extend_path(__path__, __name__) # type: ignore diff --git a/setup.py b/setup.py index d4639a90..0c9ad22e 100644 --- a/setup.py +++ b/setup.py @@ -63,15 +63,11 @@ # Only include packages under the 'google' namespace. Do not include tests, # benchmarks, etc. packages = [ - package for package in setuptools.find_packages() if package.startswith("google") + package + for package in setuptools.find_namespace_packages() + if package.startswith("google") ] -# Determine which namespaces are needed. -namespaces = ["google"] -if "google.cloud" in packages: - namespaces.append("google.cloud") - - setuptools.setup( name=name, version=version, @@ -97,7 +93,6 @@ ], platforms="Posix; MacOS X; Windows", packages=packages, - namespace_packages=namespaces, install_requires=dependencies, extras_require=extras, python_requires=">=3.7", diff --git a/tests/unit/test_packaging.py b/tests/unit/test_packaging.py new file mode 100644 index 00000000..8100a496 --- /dev/null +++ b/tests/unit/test_packaging.py @@ -0,0 +1,28 @@ +# 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 +# +# 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. + +import os +import subprocess +import sys + + +def test_namespace_package_compat(tmp_path): + # The ``google`` namespace package should not be masked + # by the presence of ``google-api-core``. + google = tmp_path / "google" + google.mkdir() + google.joinpath("othermod.py").write_text("") + env = dict(os.environ, PYTHONPATH=str(tmp_path)) + cmd = [sys.executable, "-m", "google.othermod"] + subprocess.check_call(cmd, env=env) From 9e826d9aadbf3be11ef0d03f8df254534a7a1af0 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:55:12 -0500 Subject: [PATCH 06/11] chore: bump cryptography from 41.0.5 to 41.0.6 in /synthtool/gcp/templates/python_library/.kokoro (#559) Source-Link: https://github.com/googleapis/synthtool/commit/9367caadcbb30b5b2719f30eb00c44cc913550ed Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:2f155882785883336b4468d5218db737bb1d10c9cea7cb62219ad16fe248c03c Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 +- .kokoro/requirements.txt | 48 ++++++++++---------- .kokoro/samples/python3.12/common.cfg | 40 ++++++++++++++++ .kokoro/samples/python3.12/continuous.cfg | 6 +++ .kokoro/samples/python3.12/periodic-head.cfg | 11 +++++ .kokoro/samples/python3.12/periodic.cfg | 6 +++ .kokoro/samples/python3.12/presubmit.cfg | 6 +++ 7 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 .kokoro/samples/python3.12/common.cfg create mode 100644 .kokoro/samples/python3.12/continuous.cfg create mode 100644 .kokoro/samples/python3.12/periodic-head.cfg create mode 100644 .kokoro/samples/python3.12/periodic.cfg create mode 100644 .kokoro/samples/python3.12/presubmit.cfg diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 453b540c..773c1dfd 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:caffe0a9277daeccc4d1de5c9b55ebba0901b57c2f713ec9c876b0d4ec064f61 -# created: 2023-11-08T19:46:45.022803742Z + digest: sha256:2f155882785883336b4468d5218db737bb1d10c9cea7cb62219ad16fe248c03c +# created: 2023-11-29T14:54:29.548172703Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 8957e211..e5c1ffca 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -93,30 +93,30 @@ colorlog==6.7.0 \ # via # gcp-docuploader # nox -cryptography==41.0.5 \ - --hash=sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf \ - --hash=sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84 \ - --hash=sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e \ - --hash=sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8 \ - --hash=sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7 \ - --hash=sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1 \ - --hash=sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88 \ - --hash=sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86 \ - --hash=sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179 \ - --hash=sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81 \ - --hash=sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20 \ - --hash=sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548 \ - --hash=sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d \ - --hash=sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d \ - --hash=sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5 \ - --hash=sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1 \ - --hash=sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147 \ - --hash=sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936 \ - --hash=sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797 \ - --hash=sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696 \ - --hash=sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72 \ - --hash=sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da \ - --hash=sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723 +cryptography==41.0.6 \ + --hash=sha256:068bc551698c234742c40049e46840843f3d98ad7ce265fd2bd4ec0d11306596 \ + --hash=sha256:0f27acb55a4e77b9be8d550d762b0513ef3fc658cd3eb15110ebbcbd626db12c \ + --hash=sha256:2132d5865eea673fe6712c2ed5fb4fa49dba10768bb4cc798345748380ee3660 \ + --hash=sha256:3288acccef021e3c3c10d58933f44e8602cf04dba96d9796d70d537bb2f4bbc4 \ + --hash=sha256:35f3f288e83c3f6f10752467c48919a7a94b7d88cc00b0668372a0d2ad4f8ead \ + --hash=sha256:398ae1fc711b5eb78e977daa3cbf47cec20f2c08c5da129b7a296055fbb22aed \ + --hash=sha256:422e3e31d63743855e43e5a6fcc8b4acab860f560f9321b0ee6269cc7ed70cc3 \ + --hash=sha256:48783b7e2bef51224020efb61b42704207dde583d7e371ef8fc2a5fb6c0aabc7 \ + --hash=sha256:4d03186af98b1c01a4eda396b137f29e4e3fb0173e30f885e27acec8823c1b09 \ + --hash=sha256:5daeb18e7886a358064a68dbcaf441c036cbdb7da52ae744e7b9207b04d3908c \ + --hash=sha256:60e746b11b937911dc70d164060d28d273e31853bb359e2b2033c9e93e6f3c43 \ + --hash=sha256:742ae5e9a2310e9dade7932f9576606836ed174da3c7d26bc3d3ab4bd49b9f65 \ + --hash=sha256:7e00fb556bda398b99b0da289ce7053639d33b572847181d6483ad89835115f6 \ + --hash=sha256:85abd057699b98fce40b41737afb234fef05c67e116f6f3650782c10862c43da \ + --hash=sha256:8efb2af8d4ba9dbc9c9dd8f04d19a7abb5b49eab1f3694e7b5a16a5fc2856f5c \ + --hash=sha256:ae236bb8760c1e55b7a39b6d4d32d2279bc6c7c8500b7d5a13b6fb9fc97be35b \ + --hash=sha256:afda76d84b053923c27ede5edc1ed7d53e3c9f475ebaf63c68e69f1403c405a8 \ + --hash=sha256:b27a7fd4229abef715e064269d98a7e2909ebf92eb6912a9603c7e14c181928c \ + --hash=sha256:b648fe2a45e426aaee684ddca2632f62ec4613ef362f4d681a9a6283d10e079d \ + --hash=sha256:c5a550dc7a3b50b116323e3d376241829fd326ac47bc195e04eb33a8170902a9 \ + --hash=sha256:da46e2b5df770070412c46f87bac0849b8d685c5f2679771de277a422c7d0b86 \ + --hash=sha256:f39812f70fc5c71a15aa3c97b2bbe213c3f2a460b79bd21c40d033bb34a9bf36 \ + --hash=sha256:ff369dd19e8fe0528b02e8df9f2aeb2479f89b1270d90f96a63500afe9af5cae # via # gcp-releasetool # secretstorage diff --git a/.kokoro/samples/python3.12/common.cfg b/.kokoro/samples/python3.12/common.cfg new file mode 100644 index 00000000..8a5840a7 --- /dev/null +++ b/.kokoro/samples/python3.12/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.12" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-312" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-api-core/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-api-core/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.12/continuous.cfg b/.kokoro/samples/python3.12/continuous.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.12/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.12/periodic-head.cfg b/.kokoro/samples/python3.12/periodic-head.cfg new file mode 100644 index 00000000..a18c0cfc --- /dev/null +++ b/.kokoro/samples/python3.12/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-api-core/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.12/periodic.cfg b/.kokoro/samples/python3.12/periodic.cfg new file mode 100644 index 00000000..71cd1e59 --- /dev/null +++ b/.kokoro/samples/python3.12/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} diff --git a/.kokoro/samples/python3.12/presubmit.cfg b/.kokoro/samples/python3.12/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.12/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file From 091b4f1c7fcc59c3f2a02ee44fd3c30b78423f12 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:14:32 -0500 Subject: [PATCH 07/11] feat: Add support for Python 3.12 (#557) * chore(python): Add Python 3.12 Source-Link: https://github.com/googleapis/synthtool/commit/af16e6d4672cc7b400f144de2fc3068b54ff47d2 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:bacc3af03bff793a03add584537b36b5644342931ad989e3ba1171d3bd5399f5 * Update common files to include python 3.12 --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/sync-repo-settings.yaml | 3 +++ .github/workflows/unittest.yml | 1 + CONTRIBUTING.rst | 6 ++++-- noxfile.py | 4 ++-- setup.py | 1 + 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index cdec3e9b..a19b27a7 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -16,13 +16,16 @@ branchProtectionRules: - 'unit_grpc_gcp-3.9' - 'unit_grpc_gcp-3.10' - 'unit_grpc_gcp-3.11' + - 'unit_grpc_gcp-3.12' - 'unit-3.7' - 'unit-3.8' - 'unit-3.9' - 'unit-3.10' - 'unit-3.11' + - 'unit-3.12' - 'unit_wo_grpc-3.10' - 'unit_wo_grpc-3.11' + - 'unit_wo_grpc-3.12' - 'cover' - 'docs' - 'docfx' diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 0d7789b6..497492af 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -18,6 +18,7 @@ jobs: - "3.9" - "3.10" - "3.11" + - "3.12" exclude: - option: "_wo_grpc" python: 3.7 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 13d7a516..8d1475ce 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -21,7 +21,7 @@ In order to add a feature: documentation. - The feature must work fully on the following CPython versions: - 3.7, 3.8, 3.9, 3.10, and 3.11 on both UNIX and Windows. + 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -71,7 +71,7 @@ We use `nox `__ to instrument our tests. - To run a single unit test:: - $ nox -s unit-3.11 -- -k + $ nox -s unit-3.12 -- -k .. note:: @@ -202,12 +202,14 @@ We support: - `Python 3.9`_ - `Python 3.10`_ - `Python 3.11`_ +- `Python 3.12`_ .. _Python 3.7: https://docs.python.org/3.7/ .. _Python 3.8: https://docs.python.org/3.8/ .. _Python 3.9: https://docs.python.org/3.9/ .. _Python 3.10: https://docs.python.org/3.10/ .. _Python 3.11: https://docs.python.org/3.11/ +.. _Python 3.12: https://docs.python.org/3.12/ Supported versions can be found in our ``noxfile.py`` `config`_. diff --git a/noxfile.py b/noxfile.py index d0ce6d6c..2b668e7b 100644 --- a/noxfile.py +++ b/noxfile.py @@ -149,7 +149,7 @@ def unit(session): default(session) -@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11"]) +@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]) def unit_grpc_gcp(session): """Run the unit test suite with grpcio-gcp installed.""" constraints_path = str( @@ -163,7 +163,7 @@ def unit_grpc_gcp(session): default(session) -@nox.session(python=["3.8", "3.10", "3.11"]) +@nox.session(python=["3.8", "3.10", "3.11", "3.12"]) def unit_wo_grpc(session): """Run the unit test suite w/o grpcio installed""" default(session, install_grpc=False) diff --git a/setup.py b/setup.py index 0c9ad22e..47a3c203 100644 --- a/setup.py +++ b/setup.py @@ -88,6 +88,7 @@ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Operating System :: OS Independent", "Topic :: Internet", ], From 42e8b6e6f426cab749b34906529e8aaf3f133d75 Mon Sep 17 00:00:00 2001 From: Victor Chudnovsky Date: Fri, 1 Dec 2023 14:47:21 -0800 Subject: [PATCH 08/11] chore: simplify bidi.py:_RequestQueueGenerator._is_active() (#563) Simplify using DeMorgan's law --- google/api_core/bidi.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/google/api_core/bidi.py b/google/api_core/bidi.py index 17d836d5..78d98b98 100644 --- a/google/api_core/bidi.py +++ b/google/api_core/bidi.py @@ -93,10 +93,7 @@ def _is_active(self): # property is set. So we have to check if self.call is set before # seeing if it's active. We need to return True if self.call is None. # See https://github.com/googleapis/python-api-core/issues/560. - if self.call is not None and not self.call.is_active(): - return False - else: - return True + return self.call is None or self.call.is_active() def __iter__(self): if self._initial_request is not None: From 46e878976a32d75a57e4d7995ed89629b6ef47bd Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Mon, 4 Dec 2023 15:20:21 -0500 Subject: [PATCH 09/11] build: treat warnings as errors (#564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build: treat warnings as errors * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * lint * add pytest.ini * add line break * filter warning which appears only in python 3.7 * filter deprecation warning for grpcio-gcp --------- Co-authored-by: Owl Bot --- pytest.ini | 21 +++++++++++++++++++++ tests/unit/test_iam.py | 20 ++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..66f72e41 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,21 @@ +[pytest] +filterwarnings = + # treat all warnings as errors + error + # Remove once https://github.com/pytest-dev/pytest-cov/issues/621 is fixed + ignore:.*The --rsyncdir command line argument and rsyncdirs config variable are deprecated:DeprecationWarning + # Remove once https://github.com/protocolbuffers/protobuf/issues/12186 is fixed + ignore:.*custom tp_new.*in Python 3.14:DeprecationWarning + # Remove once support for python 3.7 is dropped + # This warning only appears when using python 3.7 + ignore:.*Using or importing the ABCs from.*collections:DeprecationWarning + # Remove once support for grpcio-gcp is deprecated + # See https://github.com/googleapis/python-api-core/blob/42e8b6e6f426cab749b34906529e8aaf3f133d75/google/api_core/grpc_helpers.py#L39-L45 + ignore:.*Support for grpcio-gcp is deprecated:DeprecationWarning + # Remove once https://github.com/googleapis/python-api-common-protos/pull/187/files is merged + ignore:.*pkg_resources.declare_namespace:DeprecationWarning + ignore:.*pkg_resources is deprecated as an API:DeprecationWarning + # Remove once release PR https://github.com/googleapis/proto-plus-python/pull/391 is merged + ignore:datetime.datetime.utcfromtimestamp\(\) is deprecated:DeprecationWarning:proto.datetime_helpers + # Remove once https://github.com/grpc/grpc/issues/35086 is fixed + ignore:There is no current event loop:DeprecationWarning diff --git a/tests/unit/test_iam.py b/tests/unit/test_iam.py index fbd242e5..3de15288 100644 --- a/tests/unit/test_iam.py +++ b/tests/unit/test_iam.py @@ -167,14 +167,15 @@ def test_owners_getter(self): assert policy.owners == expected def test_owners_setter(self): - import warnings from google.api_core.iam import OWNER_ROLE MEMBER = "user:phred@example.com" expected = set([MEMBER]) policy = self._make_one() - with warnings.catch_warnings(record=True) as warned: + with pytest.warns( + DeprecationWarning, match="Assigning to 'owners' is deprecated." + ) as warned: policy.owners = [MEMBER] (warning,) = warned @@ -191,14 +192,15 @@ def test_editors_getter(self): assert policy.editors == expected def test_editors_setter(self): - import warnings from google.api_core.iam import EDITOR_ROLE MEMBER = "user:phred@example.com" expected = set([MEMBER]) policy = self._make_one() - with warnings.catch_warnings(record=True) as warned: + with pytest.warns( + DeprecationWarning, match="Assigning to 'editors' is deprecated." + ) as warned: policy.editors = [MEMBER] (warning,) = warned @@ -215,14 +217,15 @@ def test_viewers_getter(self): assert policy.viewers == expected def test_viewers_setter(self): - import warnings from google.api_core.iam import VIEWER_ROLE MEMBER = "user:phred@example.com" expected = set([MEMBER]) policy = self._make_one() - with warnings.catch_warnings(record=True) as warned: + with pytest.warns( + DeprecationWarning, match="Assigning to 'viewers' is deprecated." + ) as warned: policy.viewers = [MEMBER] (warning,) = warned @@ -337,12 +340,13 @@ def test_to_api_repr_binding_wo_members(self): assert policy.to_api_repr() == {} def test_to_api_repr_binding_w_duplicates(self): - import warnings from google.api_core.iam import OWNER_ROLE OWNER = "group:cloud-logs@google.com" policy = self._make_one() - with warnings.catch_warnings(record=True): + with pytest.warns( + DeprecationWarning, match="Assigning to 'owners' is deprecated." + ): policy.owners = [OWNER, OWNER] assert policy.to_api_repr() == { "bindings": [{"role": OWNER_ROLE, "members": [OWNER]}] From f58c9fcd477037973bbb9da5dee20f146c516fcb Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 7 Dec 2023 17:27:54 +0100 Subject: [PATCH 10/11] chore(deps): update all dependencies (#566) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- .github/workflows/lint.yml | 4 ++-- .github/workflows/mypy.yml | 4 ++-- .github/workflows/unittest.yml | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d2aee5b7..1051da0b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,9 +8,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install nox diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index a505525d..e6a79291 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -8,9 +8,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install nox diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 497492af..2cfaada3 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -28,9 +28,9 @@ jobs: python: 3.9 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install nox @@ -55,9 +55,9 @@ jobs: - run-unittests steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install coverage From 9866183b42f632dddade9ba6c69f9b3015118dd4 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 11:34:42 -0500 Subject: [PATCH 11/11] chore(main): release 2.15.0 (#555) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 16 ++++++++++++++++ google/api_core/version.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf7e3d20..0c1026c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ [1]: https://pypi.org/project/google-api-core/#history +## [2.15.0](https://github.com/googleapis/python-api-core/compare/v2.14.0...v2.15.0) (2023-12-07) + + +### Features + +* Add support for Python 3.12 ([#557](https://github.com/googleapis/python-api-core/issues/557)) ([091b4f1](https://github.com/googleapis/python-api-core/commit/091b4f1c7fcc59c3f2a02ee44fd3c30b78423f12)) +* Add type annotations to wrapped grpc calls ([#554](https://github.com/googleapis/python-api-core/issues/554)) ([fc12b40](https://github.com/googleapis/python-api-core/commit/fc12b40bfc6e0c4bb313196e2e3a9c9374ce1c45)) +* Add universe_domain argument to ClientOptions ([3069ef4](https://github.com/googleapis/python-api-core/commit/3069ef4b9123ddb64841cbb7bbb183b53d502e0a)) +* Introduce compatibility with native namespace packages ([#561](https://github.com/googleapis/python-api-core/issues/561)) ([bd82827](https://github.com/googleapis/python-api-core/commit/bd82827108f1eeb6c05cfacf6c044b2afacc18a2)) + + +### Bug Fixes + +* Fix regression in `bidi` causing `Thread-ConsumeBidirectionalStream caught unexpected exception and will exit` ([#562](https://github.com/googleapis/python-api-core/issues/562)) ([40c8ae0](https://github.com/googleapis/python-api-core/commit/40c8ae0cf1f797e31e106461164e22db4fb2d3d9)) +* Replace deprecated `datetime.datetime.utcnow()` ([#552](https://github.com/googleapis/python-api-core/issues/552)) ([448923a](https://github.com/googleapis/python-api-core/commit/448923acf277a70e8704c949311bf4feaef8cab6)), closes [#540](https://github.com/googleapis/python-api-core/issues/540) + ## [2.14.0](https://github.com/googleapis/python-api-core/compare/v2.13.1...v2.14.0) (2023-11-09) diff --git a/google/api_core/version.py b/google/api_core/version.py index ba8b4e8a..a8381fff 100644 --- a/google/api_core/version.py +++ b/google/api_core/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.14.0" +__version__ = "2.15.0"