diff --git a/.coveragerc b/.coveragerc index d097511c..34417c3f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -11,3 +11,5 @@ exclude_lines = def __repr__ # Ignore abstract methods raise NotImplementedError + # Ignore coverage for code specific to static type checkers + TYPE_CHECKING diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 02a4dedc..a3da1b0d 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:240b5bcc2bafd450912d2da2be15e62bc6de2cf839823ae4bf94d4f392b451dc -# created: 2023-06-03T21:25:37.968717478Z + digest: sha256:3e3800bb100af5d7f9e810d48212b37812c1856d20ffeafb99ebe66461b61fc7 +# created: 2023-08-02T10:53:29.114535628Z diff --git a/.github/auto-label.yaml b/.github/auto-label.yaml index 41bff0b5..b2016d11 100644 --- a/.github/auto-label.yaml +++ b/.github/auto-label.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# 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. diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 0394c8aa..05618cbc 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index f8137d0a..8e39a2cc 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -1,4 +1,4 @@ -# Copyright 2020 Google LLC +# 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. diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh index f5251425..6f397214 100755 --- a/.kokoro/populate-secrets.sh +++ b/.kokoro/populate-secrets.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2020 Google LLC. +# 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. diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh index 1c4d6237..9eafe0be 100755 --- a/.kokoro/publish-docs.sh +++ b/.kokoro/publish-docs.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2020 Google LLC +# 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. diff --git a/.kokoro/release.sh b/.kokoro/release.sh index 697f7e6d..b07000df 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2020 Google LLC +# 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. diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index de4f6f89..5890a7fe 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -38,3 +38,12 @@ env_vars: { key: "SECRET_MANAGER_KEYS" value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" } + +# Store the packages we uploaded to PyPI. That way, we have a record of exactly +# what we published, which we can use to generate SBOMs and attestations. +action { + define_artifacts { + regex: "github/python-api-core/**/*.tar.gz" + strip_prefix: "github/python-api-core" + } +} diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index c7929db6..029bd342 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -20,9 +20,9 @@ cachetools==5.2.0 \ --hash=sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757 \ --hash=sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db # via google-auth -certifi==2022.12.7 \ - --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ - --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 +certifi==2023.7.22 \ + --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ + --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 # via requests cffi==1.15.1 \ --hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \ @@ -113,26 +113,30 @@ commonmark==0.9.1 \ --hash=sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60 \ --hash=sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9 # via rich -cryptography==41.0.0 \ - --hash=sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55 \ - --hash=sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895 \ - --hash=sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be \ - --hash=sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928 \ - --hash=sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d \ - --hash=sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8 \ - --hash=sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237 \ - --hash=sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9 \ - --hash=sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78 \ - --hash=sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d \ - --hash=sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0 \ - --hash=sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46 \ - --hash=sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5 \ - --hash=sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4 \ - --hash=sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d \ - --hash=sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75 \ - --hash=sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb \ - --hash=sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2 \ - --hash=sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be +cryptography==41.0.3 \ + --hash=sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306 \ + --hash=sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84 \ + --hash=sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47 \ + --hash=sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d \ + --hash=sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116 \ + --hash=sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207 \ + --hash=sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81 \ + --hash=sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087 \ + --hash=sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd \ + --hash=sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507 \ + --hash=sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858 \ + --hash=sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae \ + --hash=sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34 \ + --hash=sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906 \ + --hash=sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd \ + --hash=sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922 \ + --hash=sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7 \ + --hash=sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4 \ + --hash=sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574 \ + --hash=sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1 \ + --hash=sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c \ + --hash=sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e \ + --hash=sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de # via # gcp-releasetool # secretstorage @@ -392,9 +396,9 @@ pycparser==2.21 \ --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 # via cffi -pygments==2.13.0 \ - --hash=sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1 \ - --hash=sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42 +pygments==2.15.0 \ + --hash=sha256:77a3299119af881904cd5ecd1ac6a66214b6e9bed1f2db16993b54adede64094 \ + --hash=sha256:f7e36cffc4c517fbc252861b9a6e4644ca0e5abadf9a113c72d1358ad09b9500 # via # readme-renderer # rich diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh index ba3a707b..63ac41df 100755 --- a/.kokoro/test-samples-against-head.sh +++ b/.kokoro/test-samples-against-head.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2020 Google LLC +# 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. diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh index 2c6500ca..5a0f5fab 100755 --- a/.kokoro/test-samples-impl.sh +++ b/.kokoro/test-samples-impl.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2021 Google LLC +# 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. diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index 11c042d3..50b35a48 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2020 Google LLC +# 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. diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh index f39236e9..d85b1f26 100755 --- a/.kokoro/trampoline.sh +++ b/.kokoro/trampoline.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2017 Google Inc. +# 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. diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 4af6cdc2..59a7cf3a 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2020 Google LLC +# 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. diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5405cc8f..19409cbd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# 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. @@ -26,6 +26,6 @@ repos: hooks: - id: black - repo: https://github.com/pycqa/flake8 - rev: 3.9.2 + rev: 6.1.0 hooks: - id: flake8 diff --git a/.trampolinerc b/.trampolinerc index 0eee72ab..a7dfeb42 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -1,4 +1,4 @@ -# Copyright 2020 Google LLC +# 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. @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Template for .trampolinerc - # Add required env vars here. required_envvars+=( ) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e27acbe..93c3848c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ [1]: https://pypi.org/project/google-api-core/#history +## [2.12.0](https://github.com/googleapis/python-api-core/compare/v2.11.1...v2.12.0) (2023-09-07) + + +### Features + +* Add a little bit of typing to google.api_core.retry ([#453](https://github.com/googleapis/python-api-core/issues/453)) ([2477ab9](https://github.com/googleapis/python-api-core/commit/2477ab9ea5c2e863a493fb7ebebaa429a44ea096)) +* Add grpc Compression argument to channels and methods ([#451](https://github.com/googleapis/python-api-core/issues/451)) ([bdebd63](https://github.com/googleapis/python-api-core/commit/bdebd6331f9c0d3d1a8ceaf274f07d2ed75bfe92)) + + +### Documentation + +* Fix a typo in google/api_core/page_iterator.py ([#511](https://github.com/googleapis/python-api-core/issues/511)) ([c0ce73c](https://github.com/googleapis/python-api-core/commit/c0ce73c4de53ad694fe36d17408998aa1230398f)) + ## [2.11.1](https://github.com/googleapis/python-api-core/compare/v2.11.0...v2.11.1) (2023-06-12) diff --git a/MANIFEST.in b/MANIFEST.in index e783f4c6..e0a66705 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2020 Google LLC +# 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. diff --git a/docs/conf.py b/docs/conf.py index 9a80171b..588e983f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2021 Google LLC +# 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. diff --git a/google/api_core/gapic_v1/method.py b/google/api_core/gapic_v1/method.py index 0c1624a3..e6df1332 100644 --- a/google/api_core/gapic_v1/method.py +++ b/google/api_core/gapic_v1/method.py @@ -15,7 +15,7 @@ """Helpers for wrapping low-level gRPC methods with common functionality. This is used by gapic clients to provide common error mapping, retry, timeout, -pagination, and long-running operations to gRPC methods. +compression, pagination, and long-running operations to gRPC methods. """ import enum @@ -38,7 +38,7 @@ class _MethodDefault(enum.Enum): DEFAULT = _MethodDefault._DEFAULT_VALUE -"""Sentinel value indicating that a retry or timeout argument was unspecified, +"""Sentinel value indicating that a retry, timeout, or compression argument was unspecified, so the default should be used.""" @@ -72,20 +72,33 @@ class _GapicCallable(object): after its start, not to be confused with deadline). If ``None``, this callable will not specify a timeout argument to the low-level RPC method. + compression (grpc.Compression): The default compression for the callable. + If ``None``, this callable will not specify a compression argument + to the low-level RPC method. metadata (Sequence[Tuple[str, str]]): Additional metadata that is provided to the RPC method on every invocation. This is merged with any metadata specified during invocation. If ``None``, no additional metadata will be passed to the RPC method. """ - def __init__(self, target, retry, timeout, metadata=None): + def __init__( + self, + target, + retry, + timeout, + compression, + metadata=None, + ): self._target = target self._retry = retry self._timeout = timeout + self._compression = compression self._metadata = metadata - def __call__(self, *args, timeout=DEFAULT, retry=DEFAULT, **kwargs): - """Invoke the low-level RPC with retry, timeout, and metadata.""" + def __call__( + self, *args, timeout=DEFAULT, retry=DEFAULT, compression=DEFAULT, **kwargs + ): + """Invoke the low-level RPC with retry, timeout, compression, and metadata.""" if retry is DEFAULT: retry = self._retry @@ -93,6 +106,9 @@ def __call__(self, *args, timeout=DEFAULT, retry=DEFAULT, **kwargs): if timeout is DEFAULT: timeout = self._timeout + if compression is DEFAULT: + compression = self._compression + if isinstance(timeout, (int, float)): timeout = TimeToDeadlineTimeout(timeout=timeout) @@ -109,6 +125,8 @@ def __call__(self, *args, timeout=DEFAULT, retry=DEFAULT, **kwargs): metadata = list(metadata) metadata.extend(self._metadata) kwargs["metadata"] = metadata + if self._compression is not None: + kwargs["compression"] = compression return wrapped_func(*args, **kwargs) @@ -117,12 +135,13 @@ def wrap_method( func, default_retry=None, default_timeout=None, + default_compression=None, client_info=client_info.DEFAULT_CLIENT_INFO, ): """Wrap an RPC method with common behavior. - This applies common error wrapping, retry, and timeout behavior a function. - The wrapped function will take optional ``retry`` and ``timeout`` + This applies common error wrapping, retry, timeout, and compression behavior to a function. + The wrapped function will take optional ``retry``, ``timeout``, and ``compression`` arguments. For example:: @@ -130,6 +149,7 @@ def wrap_method( import google.api_core.gapic_v1.method from google.api_core import retry from google.api_core import timeout + from grpc import Compression # The original RPC method. def get_topic(name, timeout=None): @@ -138,6 +158,7 @@ def get_topic(name, timeout=None): default_retry = retry.Retry(deadline=60) default_timeout = timeout.Timeout(deadline=60) + default_compression = Compression.NoCompression wrapped_get_topic = google.api_core.gapic_v1.method.wrap_method( get_topic, default_retry) @@ -186,6 +207,9 @@ def get_topic(name, timeout=None): default_timeout (Optional[google.api_core.Timeout]): The default timeout strategy. Can also be specified as an int or float. If ``None``, the method will not have timeout specified by default. + default_compression (Optional[grpc.Compression]): The default + grpc.Compression. If ``None``, the method will not have + compression specified by default. client_info (Optional[google.api_core.gapic_v1.client_info.ClientInfo]): Client information used to create a user-agent string that's @@ -194,12 +218,12 @@ def get_topic(name, timeout=None): metadata will be provided to the RPC method. Returns: - Callable: A new callable that takes optional ``retry`` and ``timeout`` - arguments and applies the common error mapping, retry, timeout, + Callable: A new callable that takes optional ``retry``, ``timeout``, + and ``compression`` + arguments and applies the common error mapping, retry, timeout, compression, and metadata behavior to the low-level RPC method. """ func = grpc_helpers.wrap_errors(func) - if client_info is not None: user_agent_metadata = [client_info.to_grpc_metadata()] else: @@ -207,6 +231,10 @@ def get_topic(name, timeout=None): return functools.wraps(func)( _GapicCallable( - func, default_retry, default_timeout, metadata=user_agent_metadata + func, + default_retry, + default_timeout, + default_compression, + metadata=user_agent_metadata, ) ) diff --git a/google/api_core/gapic_v1/method_async.py b/google/api_core/gapic_v1/method_async.py index 84c99aa2..24880756 100644 --- a/google/api_core/gapic_v1/method_async.py +++ b/google/api_core/gapic_v1/method_async.py @@ -14,7 +14,7 @@ """AsyncIO helpers for wrapping gRPC methods with common functionality. This is used by gapic clients to provide common error mapping, retry, timeout, -pagination, and long-running operations to gRPC methods. +compression, pagination, and long-running operations to gRPC methods. """ import functools @@ -30,19 +30,26 @@ def wrap_method( func, default_retry=None, default_timeout=None, + default_compression=None, client_info=client_info.DEFAULT_CLIENT_INFO, ): """Wrap an async RPC method with common behavior. Returns: - Callable: A new callable that takes optional ``retry`` and ``timeout`` - arguments and applies the common error mapping, retry, timeout, - and metadata behavior to the low-level RPC method. + Callable: A new callable that takes optional ``retry``, ``timeout``, + and ``compression`` arguments and applies the common error mapping, + retry, timeout, metadata, and compression behavior to the low-level RPC method. """ func = grpc_helpers_async.wrap_errors(func) metadata = [client_info.to_grpc_metadata()] if client_info is not None else None return functools.wraps(func)( - _GapicCallable(func, default_retry, default_timeout, metadata=metadata) + _GapicCallable( + func, + default_retry, + default_timeout, + default_compression, + metadata=metadata, + ) ) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 102dc0a0..f52e180a 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -16,6 +16,7 @@ import collections import functools +import logging import warnings import grpc @@ -51,6 +52,8 @@ # The list of gRPC Callable interfaces that return iterators. _STREAM_WRAP_CLASSES = (grpc.UnaryStreamMultiCallable, grpc.StreamStreamMultiCallable) +_LOGGER = logging.getLogger(__name__) + def _patch_callable_name(callable_): """Fix-up gRPC callable attributes. @@ -276,7 +279,8 @@ def create_channel( quota_project_id=None, default_scopes=None, default_host=None, - **kwargs + compression=None, + **kwargs, ): """Create a secure channel with credentials. @@ -297,6 +301,8 @@ def create_channel( default_scopes (Sequence[str]): Default scopes passed by a Google client library. Use 'scopes' for user-defined scopes. default_host (str): The default endpoint. e.g., "pubsub.googleapis.com". + compression (grpc.Compression): An optional value indicating the + compression method to be used over the lifetime of the channel. kwargs: Additional key-word args passed to :func:`grpc_gcp.secure_channel` or :func:`grpc.secure_channel`. Note: `grpc_gcp` is only supported in environments with protobuf < 4.0.0. @@ -319,12 +325,18 @@ def create_channel( ) if HAS_GRPC_GCP: # pragma: NO COVER + if compression is not None and compression != grpc.Compression.NoCompression: + _LOGGER.debug( + "Compression argument is being ignored for grpc_gcp.secure_channel creation." + ) return grpc_gcp.secure_channel(target, composite_credentials, **kwargs) - return grpc.secure_channel(target, composite_credentials, **kwargs) + return grpc.secure_channel( + target, composite_credentials, compression=compression, **kwargs + ) _MethodCall = collections.namedtuple( - "_MethodCall", ("request", "timeout", "metadata", "credentials") + "_MethodCall", ("request", "timeout", "metadata", "credentials", "compression") ) _ChannelRequest = collections.namedtuple("_ChannelRequest", ("method", "request")) @@ -351,11 +363,15 @@ def __init__(self, method, channel): """List[protobuf.Message]: All requests sent to this callable.""" self.calls = [] """List[Tuple]: All invocations of this callable. Each tuple is the - request, timeout, metadata, and credentials.""" + request, timeout, metadata, compression, and credentials.""" - def __call__(self, request, timeout=None, metadata=None, credentials=None): + def __call__( + self, request, timeout=None, metadata=None, credentials=None, compression=None + ): self._channel.requests.append(_ChannelRequest(self._method, request)) - self.calls.append(_MethodCall(request, timeout, metadata, credentials)) + self.calls.append( + _MethodCall(request, timeout, metadata, credentials, compression) + ) self.requests.append(request) response = self.response diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index 5a5bf2a6..d1f69d98 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -212,6 +212,7 @@ def create_channel( quota_project_id=None, default_scopes=None, default_host=None, + compression=None, **kwargs ): """Create an AsyncIO secure channel with credentials. @@ -233,6 +234,8 @@ def create_channel( default_scopes (Sequence[str]): Default scopes passed by a Google client library. Use 'scopes' for user-defined scopes. default_host (str): The default endpoint. e.g., "pubsub.googleapis.com". + compression (grpc.Compression): An optional value indicating the + compression method to be used over the lifetime of the channel. kwargs: Additional key-word args passed to :func:`aio.secure_channel`. Returns: @@ -252,7 +255,9 @@ def create_channel( default_host=default_host, ) - return aio.secure_channel(target, composite_credentials, **kwargs) + return aio.secure_channel( + target, composite_credentials, compression=compression, **kwargs + ) class FakeUnaryUnaryCall(_WrappedUnaryUnaryCall): diff --git a/google/api_core/operation.py b/google/api_core/operation.py index 90cbdc99..4b9c9a58 100644 --- a/google/api_core/operation.py +++ b/google/api_core/operation.py @@ -315,10 +315,16 @@ def from_grpc(operation, operations_stub, result_type, grpc_metadata=None, **kwa operation. """ refresh = functools.partial( - _refresh_grpc, operations_stub, operation.name, metadata=grpc_metadata + _refresh_grpc, + operations_stub, + operation.name, + metadata=grpc_metadata, ) cancel = functools.partial( - _cancel_grpc, operations_stub, operation.name, metadata=grpc_metadata + _cancel_grpc, + operations_stub, + operation.name, + metadata=grpc_metadata, ) return Operation(operation, refresh, cancel, result_type, **kwargs) @@ -347,9 +353,13 @@ def from_gapic(operation, operations_client, result_type, grpc_metadata=None, ** operation. """ refresh = functools.partial( - operations_client.get_operation, operation.name, metadata=grpc_metadata + operations_client.get_operation, + operation.name, + metadata=grpc_metadata, ) cancel = functools.partial( - operations_client.cancel_operation, operation.name, metadata=grpc_metadata + operations_client.cancel_operation, + operation.name, + metadata=grpc_metadata, ) return Operation(operation, refresh, cancel, result_type, **kwargs) diff --git a/google/api_core/operation_async.py b/google/api_core/operation_async.py index 6bae8654..2fd341d9 100644 --- a/google/api_core/operation_async.py +++ b/google/api_core/operation_async.py @@ -213,9 +213,13 @@ def from_gapic(operation, operations_client, result_type, grpc_metadata=None, ** operation. """ refresh = functools.partial( - operations_client.get_operation, operation.name, metadata=grpc_metadata + operations_client.get_operation, + operation.name, + metadata=grpc_metadata, ) cancel = functools.partial( - operations_client.cancel_operation, operation.name, metadata=grpc_metadata + operations_client.cancel_operation, + operation.name, + metadata=grpc_metadata, ) return AsyncOperation(operation, refresh, cancel, result_type, **kwargs) diff --git a/google/api_core/operations_v1/abstract_operations_client.py b/google/api_core/operations_v1/abstract_operations_client.py index 78a61746..714c2aae 100644 --- a/google/api_core/operations_v1/abstract_operations_client.py +++ b/google/api_core/operations_v1/abstract_operations_client.py @@ -33,6 +33,7 @@ from google.auth.transport import mtls # type: ignore from google.longrunning import operations_pb2 from google.oauth2 import service_account # type: ignore +import grpc OptionalRetry = Union[retries.Retry, object] @@ -368,6 +369,7 @@ def list_operations( page_token: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, + compression: Optional[grpc.Compression] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> pagers.ListOperationsPager: r"""Lists operations that match the specified filter in the request. @@ -429,6 +431,7 @@ def list_operations( request, retry=retry, timeout=timeout, + compression=compression, metadata=metadata, ) @@ -450,6 +453,7 @@ def get_operation( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, + compression: Optional[grpc.Compression] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> operations_pb2.Operation: r"""Gets the latest state of a long-running operation. @@ -490,6 +494,7 @@ def get_operation( request, retry=retry, timeout=timeout, + compression=compression, metadata=metadata, ) @@ -502,6 +507,7 @@ def delete_operation( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, + compression: Optional[grpc.Compression] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> None: r"""Deletes a long-running operation. This method indicates that the @@ -541,6 +547,7 @@ def delete_operation( request, retry=retry, timeout=timeout, + compression=compression, metadata=metadata, ) @@ -550,6 +557,7 @@ def cancel_operation( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, + compression: Optional[grpc.Compression] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> None: r"""Starts asynchronous cancellation on a long-running operation. @@ -598,5 +606,6 @@ def cancel_operation( request, retry=retry, timeout=timeout, + compression=compression, metadata=metadata, ) diff --git a/google/api_core/operations_v1/operations_async_client.py b/google/api_core/operations_v1/operations_async_client.py index 81c4513c..72c68c70 100644 --- a/google/api_core/operations_v1/operations_async_client.py +++ b/google/api_core/operations_v1/operations_async_client.py @@ -29,6 +29,7 @@ from google.api_core import retry as retries from google.api_core import timeout as timeouts from google.longrunning import operations_pb2 +from grpc import Compression class OperationsAsyncClient: @@ -59,28 +60,34 @@ def __init__(self, channel, client_config=None): ) default_timeout = timeouts.TimeToDeadlineTimeout(timeout=600.0) + default_compression = Compression.NoCompression + self._get_operation = gapic_v1.method_async.wrap_method( self.operations_stub.GetOperation, default_retry=default_retry, default_timeout=default_timeout, + default_compression=default_compression, ) self._list_operations = gapic_v1.method_async.wrap_method( self.operations_stub.ListOperations, default_retry=default_retry, default_timeout=default_timeout, + default_compression=default_compression, ) self._cancel_operation = gapic_v1.method_async.wrap_method( self.operations_stub.CancelOperation, default_retry=default_retry, default_timeout=default_timeout, + default_compression=default_compression, ) self._delete_operation = gapic_v1.method_async.wrap_method( self.operations_stub.DeleteOperation, default_retry=default_retry, default_timeout=default_timeout, + default_compression=default_compression, ) async def get_operation( @@ -88,6 +95,7 @@ async def get_operation( name, retry=gapic_v1.method_async.DEFAULT, timeout=gapic_v1.method_async.DEFAULT, + compression=gapic_v1.method_async.DEFAULT, metadata=None, ): """Gets the latest state of a long-running operation. @@ -114,6 +122,8 @@ async def get_operation( unspecified, the the default timeout in the client configuration is used. If ``None``, then the RPC method will not time out. + compression (grpc.Compression): An element of grpc.compression + e.g. grpc.compression.Gzip. metadata (Optional[List[Tuple[str, str]]]): Additional gRPC metadata. @@ -133,7 +143,11 @@ async def get_operation( metadata.append(gapic_v1.routing_header.to_grpc_metadata({"name": name})) return await self._get_operation( - request, retry=retry, timeout=timeout, metadata=metadata + request, + retry=retry, + timeout=timeout, + compression=compression, + metadata=metadata, ) async def list_operations( @@ -142,6 +156,7 @@ async def list_operations( filter_, retry=gapic_v1.method_async.DEFAULT, timeout=gapic_v1.method_async.DEFAULT, + compression=gapic_v1.method_async.DEFAULT, metadata=None, ): """ @@ -178,6 +193,8 @@ async def list_operations( unspecified, the the default timeout in the client configuration is used. If ``None``, then the RPC method will not time out. + compression (grpc.Compression): An element of grpc.compression + e.g. grpc.compression.Gzip. metadata (Optional[List[Tuple[str, str]]]): Additional gRPC metadata. @@ -202,7 +219,11 @@ async def list_operations( # Create the method used to fetch pages method = functools.partial( - self._list_operations, retry=retry, timeout=timeout, metadata=metadata + self._list_operations, + retry=retry, + timeout=timeout, + compression=compression, + metadata=metadata, ) iterator = page_iterator_async.AsyncGRPCIterator( @@ -221,6 +242,7 @@ async def cancel_operation( name, retry=gapic_v1.method_async.DEFAULT, timeout=gapic_v1.method_async.DEFAULT, + compression=gapic_v1.method_async.DEFAULT, metadata=None, ): """Starts asynchronous cancellation on a long-running operation. @@ -261,6 +283,8 @@ async def cancel_operation( google.api_core.exceptions.GoogleAPICallError: If an error occurred while invoking the RPC, the appropriate ``GoogleAPICallError`` subclass will be raised. + compression (grpc.Compression): An element of grpc.compression + e.g. grpc.compression.Gzip. metadata (Optional[List[Tuple[str, str]]]): Additional gRPC metadata. """ @@ -272,7 +296,11 @@ async def cancel_operation( metadata.append(gapic_v1.routing_header.to_grpc_metadata({"name": name})) await self._cancel_operation( - request, retry=retry, timeout=timeout, metadata=metadata + request, + retry=retry, + timeout=timeout, + compression=compression, + metadata=metadata, ) async def delete_operation( @@ -280,6 +308,7 @@ async def delete_operation( name, retry=gapic_v1.method_async.DEFAULT, timeout=gapic_v1.method_async.DEFAULT, + compression=gapic_v1.method_async.DEFAULT, metadata=None, ): """Deletes a long-running operation. @@ -306,6 +335,8 @@ async def delete_operation( unspecified, the the default timeout in the client configuration is used. If ``None``, then the RPC method will not time out. + compression (grpc.Compression): An element of grpc.compression + e.g. grpc.compression.Gzip. metadata (Optional[List[Tuple[str, str]]]): Additional gRPC metadata. @@ -325,5 +356,9 @@ async def delete_operation( metadata.append(gapic_v1.routing_header.to_grpc_metadata({"name": name})) await self._delete_operation( - request, retry=retry, timeout=timeout, metadata=metadata + request, + retry=retry, + timeout=timeout, + compression=compression, + metadata=metadata, ) diff --git a/google/api_core/operations_v1/operations_client.py b/google/api_core/operations_v1/operations_client.py index 3ddd3c47..d1d3fd55 100644 --- a/google/api_core/operations_v1/operations_client.py +++ b/google/api_core/operations_v1/operations_client.py @@ -43,6 +43,7 @@ from google.api_core import retry as retries from google.api_core import timeout as timeouts from google.longrunning import operations_pb2 +from grpc import Compression class OperationsClient(object): @@ -72,28 +73,34 @@ def __init__(self, channel, client_config=None): ) default_timeout = timeouts.TimeToDeadlineTimeout(timeout=600.0) + default_compression = Compression.NoCompression + self._get_operation = gapic_v1.method.wrap_method( self.operations_stub.GetOperation, default_retry=default_retry, default_timeout=default_timeout, + default_compression=default_compression, ) self._list_operations = gapic_v1.method.wrap_method( self.operations_stub.ListOperations, default_retry=default_retry, default_timeout=default_timeout, + default_compression=default_compression, ) self._cancel_operation = gapic_v1.method.wrap_method( self.operations_stub.CancelOperation, default_retry=default_retry, default_timeout=default_timeout, + default_compression=default_compression, ) self._delete_operation = gapic_v1.method.wrap_method( self.operations_stub.DeleteOperation, default_retry=default_retry, default_timeout=default_timeout, + default_compression=default_compression, ) # Service calls @@ -102,6 +109,7 @@ def get_operation( name, retry=gapic_v1.method.DEFAULT, timeout=gapic_v1.method.DEFAULT, + compression=gapic_v1.method.DEFAULT, metadata=None, ): """Gets the latest state of a long-running operation. @@ -128,6 +136,8 @@ def get_operation( unspecified, the the default timeout in the client configuration is used. If ``None``, then the RPC method will not time out. + compression (grpc.Compression): An element of grpc.compression + e.g. grpc.compression.Gzip. metadata (Optional[List[Tuple[str, str]]]): Additional gRPC metadata. @@ -147,7 +157,11 @@ def get_operation( metadata.append(gapic_v1.routing_header.to_grpc_metadata({"name": name})) return self._get_operation( - request, retry=retry, timeout=timeout, metadata=metadata + request, + retry=retry, + timeout=timeout, + compression=compression, + metadata=metadata, ) def list_operations( @@ -156,6 +170,7 @@ def list_operations( filter_, retry=gapic_v1.method.DEFAULT, timeout=gapic_v1.method.DEFAULT, + compression=gapic_v1.method.DEFAULT, metadata=None, ): """ @@ -192,6 +207,8 @@ def list_operations( unspecified, the the default timeout in the client configuration is used. If ``None``, then the RPC method will not time out. + compression (grpc.Compression): An element of grpc.compression + e.g. grpc.compression.Gzip. metadata (Optional[List[Tuple[str, str]]]): Additional gRPC metadata. @@ -216,7 +233,11 @@ def list_operations( # Create the method used to fetch pages method = functools.partial( - self._list_operations, retry=retry, timeout=timeout, metadata=metadata + self._list_operations, + retry=retry, + timeout=timeout, + compression=compression, + metadata=metadata, ) iterator = page_iterator.GRPCIterator( @@ -235,6 +256,7 @@ def cancel_operation( name, retry=gapic_v1.method.DEFAULT, timeout=gapic_v1.method.DEFAULT, + compression=gapic_v1.method.DEFAULT, metadata=None, ): """Starts asynchronous cancellation on a long-running operation. @@ -267,6 +289,8 @@ def cancel_operation( unspecified, the the default timeout in the client configuration is used. If ``None``, then the RPC method will not time out. + compression (grpc.Compression): An element of grpc.compression + e.g. grpc.compression.Gzip. metadata (Optional[List[Tuple[str, str]]]): Additional gRPC metadata. @@ -285,13 +309,20 @@ def cancel_operation( metadata = metadata or [] metadata.append(gapic_v1.routing_header.to_grpc_metadata({"name": name})) - self._cancel_operation(request, retry=retry, timeout=timeout, metadata=metadata) + self._cancel_operation( + request, + retry=retry, + timeout=timeout, + compression=compression, + metadata=metadata, + ) def delete_operation( self, name, retry=gapic_v1.method.DEFAULT, timeout=gapic_v1.method.DEFAULT, + compression=gapic_v1.method.DEFAULT, metadata=None, ): """Deletes a long-running operation. @@ -318,6 +349,8 @@ def delete_operation( unspecified, the the default timeout in the client configuration is used. If ``None``, then the RPC method will not time out. + compression (grpc.Compression): An element of grpc.compression + e.g. grpc.compression.Gzip. metadata (Optional[List[Tuple[str, str]]]): Additional gRPC metadata. @@ -336,4 +369,10 @@ def delete_operation( metadata = metadata or [] metadata.append(gapic_v1.routing_header.to_grpc_metadata({"name": name})) - self._delete_operation(request, retry=retry, timeout=timeout, metadata=metadata) + self._delete_operation( + request, + retry=retry, + timeout=timeout, + compression=compression, + metadata=metadata, + ) diff --git a/google/api_core/operations_v1/transports/base.py b/google/api_core/operations_v1/transports/base.py index e19bc3e8..98cf7896 100644 --- a/google/api_core/operations_v1/transports/base.py +++ b/google/api_core/operations_v1/transports/base.py @@ -26,6 +26,7 @@ from google.longrunning import operations_pb2 from google.oauth2 import service_account # type: ignore from google.protobuf import empty_pb2 # type: ignore +from grpc import Compression DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( @@ -129,6 +130,7 @@ def _prep_wrapped_messages(self, client_info): deadline=10.0, ), default_timeout=10.0, + default_compression=Compression.NoCompression, client_info=client_info, ), self.get_operation: gapic_v1.method.wrap_method( @@ -143,6 +145,7 @@ def _prep_wrapped_messages(self, client_info): deadline=10.0, ), default_timeout=10.0, + default_compression=Compression.NoCompression, client_info=client_info, ), self.delete_operation: gapic_v1.method.wrap_method( @@ -157,6 +160,7 @@ def _prep_wrapped_messages(self, client_info): deadline=10.0, ), default_timeout=10.0, + default_compression=Compression.NoCompression, client_info=client_info, ), self.cancel_operation: gapic_v1.method.wrap_method( @@ -171,6 +175,7 @@ def _prep_wrapped_messages(self, client_info): deadline=10.0, ), default_timeout=10.0, + default_compression=Compression.NoCompression, client_info=client_info, ), } diff --git a/google/api_core/operations_v1/transports/rest.py b/google/api_core/operations_v1/transports/rest.py index bb9000f4..49f99d21 100644 --- a/google/api_core/operations_v1/transports/rest.py +++ b/google/api_core/operations_v1/transports/rest.py @@ -29,6 +29,7 @@ from google.longrunning import operations_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.protobuf import json_format # type: ignore +import grpc from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO, OperationsTransport OptionalRetry = Union[retries.Retry, object] @@ -149,6 +150,7 @@ def _list_operations( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, + compression: Optional[grpc.Compression] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> operations_pb2.ListOperationsResponse: r"""Call the list operations method over HTTP. @@ -228,6 +230,7 @@ def _get_operation( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, + compression: Optional[grpc.Compression] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> operations_pb2.Operation: r"""Call the get operation method over HTTP. @@ -308,6 +311,7 @@ def _delete_operation( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, + compression: Optional[grpc.Compression] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> empty_pb2.Empty: r"""Call the delete operation method over HTTP. @@ -378,6 +382,7 @@ def _cancel_operation( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, + compression: Optional[grpc.Compression] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> empty_pb2.Empty: r"""Call the cancel operation method over HTTP. diff --git a/google/api_core/page_iterator.py b/google/api_core/page_iterator.py index 7ddc5cbc..23761ec4 100644 --- a/google/api_core/page_iterator.py +++ b/google/api_core/page_iterator.py @@ -448,7 +448,7 @@ class _GAXIterator(Iterator): page_iter (google.gax.PageIterator): A GAX page iterator to be wrapped to conform to the :class:`Iterator` interface. item_to_value (Callable[Iterator, Any]): Callable to convert an item - from the the protobuf response into a native object. Will + from the protobuf response into a native object. Will be called with the iterator and a single item. max_results (int): The maximum number of results to fetch. diff --git a/google/api_core/protobuf_helpers.py b/google/api_core/protobuf_helpers.py index f1abf068..d777c5f8 100644 --- a/google/api_core/protobuf_helpers.py +++ b/google/api_core/protobuf_helpers.py @@ -313,7 +313,7 @@ def field_mask(original, modified): modified = copy.deepcopy(original) modified.Clear() - if type(original) != type(modified): + if not isinstance(original, type(modified)): raise ValueError( "expected that both original and modified should be of the " 'same type, received "{!r}" and "{!r}".'.format( diff --git a/google/api_core/retry.py b/google/api_core/retry.py index df1e65e0..84b5d0fe 100644 --- a/google/api_core/retry.py +++ b/google/api_core/retry.py @@ -54,13 +54,15 @@ def check_if_exists(): """ -from __future__ import unicode_literals +from __future__ import annotations import datetime import functools import logging import random +import sys import time +from typing import Any, Callable, TypeVar, TYPE_CHECKING import requests.exceptions @@ -68,6 +70,15 @@ def check_if_exists(): from google.api_core import exceptions from google.auth import exceptions as auth_exceptions +if TYPE_CHECKING: + if sys.version_info >= (3, 10): + from typing import ParamSpec + else: + from typing_extensions import ParamSpec + + _P = ParamSpec("_P") + _R = TypeVar("_R") + _LOGGER = logging.getLogger(__name__) _DEFAULT_INITIAL_DELAY = 1.0 # seconds _DEFAULT_MAXIMUM_DELAY = 60.0 # seconds @@ -75,7 +86,9 @@ def check_if_exists(): _DEFAULT_DEADLINE = 60.0 * 2.0 # seconds -def if_exception_type(*exception_types): +def if_exception_type( + *exception_types: type[BaseException], +) -> Callable[[BaseException], bool]: """Creates a predicate to check if the exception is of a given type. Args: @@ -87,7 +100,7 @@ def if_exception_type(*exception_types): exception is of the given type(s). """ - def if_exception_type_predicate(exception): + def if_exception_type_predicate(exception: BaseException) -> bool: """Bound predicate for checking an exception type.""" return isinstance(exception, exception_types) @@ -307,14 +320,14 @@ class Retry(object): def __init__( self, - predicate=if_transient_error, - initial=_DEFAULT_INITIAL_DELAY, - maximum=_DEFAULT_MAXIMUM_DELAY, - multiplier=_DEFAULT_DELAY_MULTIPLIER, - timeout=_DEFAULT_DEADLINE, - on_error=None, - **kwargs - ): + predicate: Callable[[BaseException], bool] = if_transient_error, + initial: float = _DEFAULT_INITIAL_DELAY, + maximum: float = _DEFAULT_MAXIMUM_DELAY, + multiplier: float = _DEFAULT_DELAY_MULTIPLIER, + timeout: float = _DEFAULT_DEADLINE, + on_error: Callable[[BaseException], Any] | None = None, + **kwargs: Any, + ) -> None: self._predicate = predicate self._initial = initial self._multiplier = multiplier @@ -323,7 +336,11 @@ def __init__( self._deadline = self._timeout self._on_error = on_error - def __call__(self, func, on_error=None): + def __call__( + self, + func: Callable[_P, _R], + on_error: Callable[[BaseException], Any] | None = None, + ) -> Callable[_P, _R]: """Wrap a callable with retry behavior. Args: @@ -340,7 +357,7 @@ def __call__(self, func, on_error=None): on_error = self._on_error @functools.wraps(func) - def retry_wrapped_func(*args, **kwargs): + def retry_wrapped_func(*args: _P.args, **kwargs: _P.kwargs) -> _R: """A wrapper that calls target function with retry.""" target = functools.partial(func, *args, **kwargs) sleep_generator = exponential_sleep_generator( diff --git a/google/api_core/version.py b/google/api_core/version.py index 4ddd9c79..67e043bd 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.11.1" +__version__ = "2.12.0" diff --git a/noxfile.py b/noxfile.py index 748485a7..84abb498 100644 --- a/noxfile.py +++ b/noxfile.py @@ -97,9 +97,7 @@ def default(session, install_grpc=True): session.install( "dataclasses", "mock", - # Revert to just "pytest" once - # https://github.com/pytest-dev/pytest/issues/10451 is fixed - "pytest<7.2.0", + "pytest", "pytest-cov", "pytest-xdist", ) @@ -239,9 +237,7 @@ def docfx(session): """Build the docfx yaml files for this library.""" session.install("-e", ".") - session.install( - "sphinx==4.0.1", "alabaster", "recommonmark", "gcp-sphinx-docfx-yaml" - ) + session.install("alabaster", "recommonmark", "gcp-sphinx-docfx-yaml") shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh index 21f6d2a2..0018b421 100755 --- a/scripts/decrypt-secrets.sh +++ b/scripts/decrypt-secrets.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2023 Google LLC All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py index 91b59676..1acc1198 100644 --- a/scripts/readme-gen/readme_gen.py +++ b/scripts/readme-gen/readme_gen.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright 2016 Google Inc +# 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. @@ -33,17 +33,17 @@ autoescape=True, ) -README_TMPL = jinja_env.get_template('README.tmpl.rst') +README_TMPL = jinja_env.get_template("README.tmpl.rst") def get_help(file): - return subprocess.check_output(['python', file, '--help']).decode() + return subprocess.check_output(["python", file, "--help"]).decode() def main(): parser = argparse.ArgumentParser() - parser.add_argument('source') - parser.add_argument('--destination', default='README.rst') + parser.add_argument("source") + parser.add_argument("--destination", default="README.rst") args = parser.parse_args() @@ -51,9 +51,9 @@ def main(): root = os.path.dirname(source) destination = os.path.join(root, args.destination) - jinja_env.globals['get_help'] = get_help + jinja_env.globals["get_help"] = get_help - with io.open(source, 'r') as f: + with io.open(source, "r") as f: config = yaml.load(f) # This allows get_help to execute in the right directory. @@ -61,9 +61,9 @@ def main(): output = README_TMPL.render(config) - with io.open(destination, 'w') as f: + with io.open(destination, "w") as f: f.write(output) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tests/asyncio/future/test_async_future.py b/tests/asyncio/future/test_async_future.py index 1e9ae334..0cfe6773 100644 --- a/tests/asyncio/future/test_async_future.py +++ b/tests/asyncio/future/test_async_future.py @@ -47,7 +47,6 @@ async def test_polling_future_constructor(): @pytest.mark.asyncio async def test_set_result(): future = AsyncFuture() - callback = mock.Mock() future.set_result(1) diff --git a/tests/asyncio/gapic/test_method_async.py b/tests/asyncio/gapic/test_method_async.py index 02d883f6..ee206979 100644 --- a/tests/asyncio/gapic/test_method_async.py +++ b/tests/asyncio/gapic/test_method_async.py @@ -18,7 +18,7 @@ import pytest try: - from grpc import aio + from grpc import aio, Compression except ImportError: pytest.skip("No GRPC", allow_module_level=True) @@ -92,6 +92,35 @@ async def test_wrap_method_with_custom_client_info(): assert client_info.to_grpc_metadata() in metadata +@pytest.mark.asyncio +async def test_wrap_method_with_no_compression(): + fake_call = grpc_helpers_async.FakeUnaryUnaryCall() + method = mock.Mock(spec=aio.UnaryUnaryMultiCallable, return_value=fake_call) + + wrapped_method = gapic_v1.method_async.wrap_method(method) + + await wrapped_method(1, 2, meep="moop", compression=None) + + method.assert_called_once_with(1, 2, meep="moop", metadata=mock.ANY) + + +@pytest.mark.asyncio +async def test_wrap_method_with_custom_compression(): + compression = Compression.Gzip + fake_call = grpc_helpers_async.FakeUnaryUnaryCall() + method = mock.Mock(spec=aio.UnaryUnaryMultiCallable, return_value=fake_call) + + wrapped_method = gapic_v1.method_async.wrap_method( + method, default_compression=compression + ) + + await wrapped_method(1, 2, meep="moop", compression=Compression.Deflate) + + method.assert_called_once_with( + 1, 2, meep="moop", metadata=mock.ANY, compression=Compression.Deflate + ) + + @pytest.mark.asyncio async def test_invoke_wrapped_method_with_metadata(): fake_call = grpc_helpers_async.FakeUnaryUnaryCall() @@ -126,7 +155,7 @@ async def test_invoke_wrapped_method_with_metadata_as_none(): @mock.patch("asyncio.sleep") @pytest.mark.asyncio -async def test_wrap_method_with_default_retry_and_timeout(unused_sleep): +async def test_wrap_method_with_default_retry_timeout_and_compression(unused_sleep): fake_call = grpc_helpers_async.FakeUnaryUnaryCall(42) method = mock.Mock( spec=aio.UnaryUnaryMultiCallable, @@ -135,15 +164,18 @@ async def test_wrap_method_with_default_retry_and_timeout(unused_sleep): default_retry = retry_async.AsyncRetry() default_timeout = timeout.ConstantTimeout(60) + default_compression = Compression.Gzip wrapped_method = gapic_v1.method_async.wrap_method( - method, default_retry, default_timeout + method, default_retry, default_timeout, default_compression ) result = await wrapped_method() assert result == 42 assert method.call_count == 2 - method.assert_called_with(timeout=60, metadata=mock.ANY) + method.assert_called_with( + timeout=60, compression=default_compression, metadata=mock.ANY + ) @mock.patch("asyncio.sleep") @@ -157,23 +189,27 @@ async def test_wrap_method_with_default_retry_and_timeout_using_sentinel(unused_ default_retry = retry_async.AsyncRetry() default_timeout = timeout.ConstantTimeout(60) + default_compression = Compression.Gzip wrapped_method = gapic_v1.method_async.wrap_method( - method, default_retry, default_timeout + method, default_retry, default_timeout, default_compression ) result = await wrapped_method( retry=gapic_v1.method_async.DEFAULT, timeout=gapic_v1.method_async.DEFAULT, + compression=gapic_v1.method_async.DEFAULT, ) assert result == 42 assert method.call_count == 2 - method.assert_called_with(timeout=60, metadata=mock.ANY) + method.assert_called_with( + timeout=60, compression=Compression.Gzip, metadata=mock.ANY + ) @mock.patch("asyncio.sleep") @pytest.mark.asyncio -async def test_wrap_method_with_overriding_retry_and_timeout(unused_sleep): +async def test_wrap_method_with_overriding_retry_timeout_and_compression(unused_sleep): fake_call = grpc_helpers_async.FakeUnaryUnaryCall(42) method = mock.Mock( spec=aio.UnaryUnaryMultiCallable, @@ -182,8 +218,9 @@ async def test_wrap_method_with_overriding_retry_and_timeout(unused_sleep): default_retry = retry_async.AsyncRetry() default_timeout = timeout.ConstantTimeout(60) + default_compression = Compression.Gzip wrapped_method = gapic_v1.method_async.wrap_method( - method, default_retry, default_timeout + method, default_retry, default_timeout, default_compression ) result = await wrapped_method( @@ -191,11 +228,14 @@ async def test_wrap_method_with_overriding_retry_and_timeout(unused_sleep): retry_async.if_exception_type(exceptions.NotFound) ), timeout=timeout.ConstantTimeout(22), + compression=Compression.Deflate, ) assert result == 42 assert method.call_count == 2 - method.assert_called_with(timeout=22, metadata=mock.ANY) + method.assert_called_with( + timeout=22, compression=Compression.Deflate, metadata=mock.ANY + ) @pytest.mark.asyncio diff --git a/tests/asyncio/operations_v1/test_operations_async_client.py b/tests/asyncio/operations_v1/test_operations_async_client.py index 34236da7..19ac9b56 100644 --- a/tests/asyncio/operations_v1/test_operations_async_client.py +++ b/tests/asyncio/operations_v1/test_operations_async_client.py @@ -16,7 +16,7 @@ import pytest try: - from grpc import aio + from grpc import aio, Compression except ImportError: # pragma: NO COVER pytest.skip("No GRPC", allow_module_level=True) @@ -42,10 +42,13 @@ async def test_get_operation(): ) client = operations_v1.OperationsAsyncClient(mocked_channel) - response = await client.get_operation("name", metadata=[("header", "foo")]) + response = await client.get_operation( + "name", metadata=[("header", "foo")], compression=Compression.Gzip + ) assert method.call_count == 1 assert tuple(method.call_args_list[0])[0][0].name == "name" assert ("header", "foo") in tuple(method.call_args_list[0])[1]["metadata"] + assert tuple(method.call_args_list[0])[1]["compression"] == Compression.Gzip assert ("x-goog-request-params", "name=name") in tuple(method.call_args_list[0])[1][ "metadata" ] @@ -63,7 +66,9 @@ async def test_list_operations(): mocked_channel, method, fake_call = _mock_grpc_objects(list_response) client = operations_v1.OperationsAsyncClient(mocked_channel) - pager = await client.list_operations("name", "filter", metadata=[("header", "foo")]) + pager = await client.list_operations( + "name", "filter", metadata=[("header", "foo")], compression=Compression.Gzip + ) assert isinstance(pager, page_iterator_async.AsyncIterator) responses = [] @@ -74,6 +79,7 @@ async def test_list_operations(): assert method.call_count == 1 assert ("header", "foo") in tuple(method.call_args_list[0])[1]["metadata"] + assert tuple(method.call_args_list[0])[1]["compression"] == Compression.Gzip assert ("x-goog-request-params", "name=name") in tuple(method.call_args_list[0])[1][ "metadata" ] @@ -88,11 +94,14 @@ async def test_delete_operation(): mocked_channel, method, fake_call = _mock_grpc_objects(empty_pb2.Empty()) client = operations_v1.OperationsAsyncClient(mocked_channel) - await client.delete_operation("name", metadata=[("header", "foo")]) + await client.delete_operation( + "name", metadata=[("header", "foo")], compression=Compression.Gzip + ) assert method.call_count == 1 assert tuple(method.call_args_list[0])[0][0].name == "name" assert ("header", "foo") in tuple(method.call_args_list[0])[1]["metadata"] + assert tuple(method.call_args_list[0])[1]["compression"] == Compression.Gzip assert ("x-goog-request-params", "name=name") in tuple(method.call_args_list[0])[1][ "metadata" ] @@ -103,11 +112,14 @@ async def test_cancel_operation(): mocked_channel, method, fake_call = _mock_grpc_objects(empty_pb2.Empty()) client = operations_v1.OperationsAsyncClient(mocked_channel) - await client.cancel_operation("name", metadata=[("header", "foo")]) + await client.cancel_operation( + "name", metadata=[("header", "foo")], compression=Compression.Gzip + ) assert method.call_count == 1 assert tuple(method.call_args_list[0])[0][0].name == "name" assert ("header", "foo") in tuple(method.call_args_list[0])[1]["metadata"] + assert tuple(method.call_args_list[0])[1]["compression"] == Compression.Gzip assert ("x-goog-request-params", "name=name") in tuple(method.call_args_list[0])[1][ "metadata" ] diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py index 33ce8212..95242f6b 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -292,7 +292,9 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c assert channel is grpc_secure_channel.return_value default.assert_called_once_with(scopes=None, default_scopes=None) - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("google.auth.transport.grpc.AuthMetadataPlugin", autospec=True) @@ -323,7 +325,9 @@ def test_create_channel_implicit_with_default_host( auth_metadata_plugin.assert_called_once_with( mock.sentinel.credentials, mock.sentinel.Request, default_host=default_host ) - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -344,7 +348,9 @@ def test_create_channel_implicit_with_ssl_creds( default.assert_called_once_with(scopes=None, default_scopes=None) composite_creds_call.assert_called_once_with(ssl_creds, mock.ANY) composite_creds = composite_creds_call.return_value - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -365,7 +371,9 @@ def test_create_channel_implicit_with_scopes( assert channel is grpc_secure_channel.return_value default.assert_called_once_with(scopes=["one", "two"], default_scopes=None) - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -382,13 +390,15 @@ def test_create_channel_implicit_with_default_scopes( composite_creds = composite_creds_call.return_value channel = grpc_helpers_async.create_channel( - target, default_scopes=["three", "four"] + target, default_scopes=["three", "four"], compression=grpc.Compression.Gzip ) assert channel is grpc_secure_channel.return_value default.assert_called_once_with(scopes=None, default_scopes=["three", "four"]) - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=grpc.Compression.Gzip + ) def test_create_channel_explicit_with_duplicate_credentials(): @@ -412,14 +422,16 @@ def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_cred composite_creds = composite_creds_call.return_value channel = grpc_helpers_async.create_channel( - target, credentials=mock.sentinel.credentials + target, credentials=mock.sentinel.credentials, compression=grpc.Compression.Gzip ) auth_creds.assert_called_once_with( mock.sentinel.credentials, scopes=None, default_scopes=None ) assert channel is grpc_secure_channel.return_value - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=grpc.Compression.Gzip + ) @mock.patch("grpc.composite_channel_credentials") @@ -433,12 +445,17 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal credentials.requires_scopes = True channel = grpc_helpers_async.create_channel( - target, credentials=credentials, scopes=scopes + target, + credentials=credentials, + scopes=scopes, + compression=grpc.Compression.Gzip, ) credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) assert channel is grpc_secure_channel.return_value - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=grpc.Compression.Gzip + ) @mock.patch("grpc.composite_channel_credentials") @@ -454,14 +471,19 @@ def test_create_channel_explicit_default_scopes( credentials.requires_scopes = True channel = grpc_helpers_async.create_channel( - target, credentials=credentials, default_scopes=default_scopes + target, + credentials=credentials, + default_scopes=default_scopes, + compression=grpc.Compression.Gzip, ) credentials.with_scopes.assert_called_once_with( scopes=None, default_scopes=default_scopes ) assert channel is grpc_secure_channel.return_value - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=grpc.Compression.Gzip + ) @mock.patch("grpc.composite_channel_credentials") @@ -482,7 +504,9 @@ def test_create_channel_explicit_with_quota_project( credentials.with_quota_project.assert_called_once_with("project-foo") assert channel is grpc_secure_channel.return_value - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -508,7 +532,9 @@ def test_create_channel_with_credentials_file( credentials_file, scopes=None, default_scopes=None ) assert channel is grpc_secure_channel.return_value - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -535,7 +561,9 @@ def test_create_channel_with_credentials_file_and_scopes( credentials_file, scopes=scopes, default_scopes=None ) assert channel is grpc_secure_channel.return_value - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -562,7 +590,9 @@ def test_create_channel_with_credentials_file_and_default_scopes( credentials_file, scopes=None, default_scopes=default_scopes ) assert channel is grpc_secure_channel.return_value - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.aio.secure_channel") diff --git a/tests/unit/gapic/test_method.py b/tests/unit/gapic/test_method.py index 03c2260d..0623a5bc 100644 --- a/tests/unit/gapic/test_method.py +++ b/tests/unit/gapic/test_method.py @@ -121,21 +121,24 @@ def test_invoke_wrapped_method_with_metadata_as_none(): @mock.patch("time.sleep") -def test_wrap_method_with_default_retry_and_timeout(unused_sleep): +def test_wrap_method_with_default_retry_and_timeout_and_compression(unused_sleep): method = mock.Mock( spec=["__call__"], side_effect=[exceptions.InternalServerError(None), 42] ) default_retry = retry.Retry() default_timeout = timeout.ConstantTimeout(60) + default_compression = grpc.Compression.Gzip wrapped_method = google.api_core.gapic_v1.method.wrap_method( - method, default_retry, default_timeout + method, default_retry, default_timeout, default_compression ) result = wrapped_method() assert result == 42 assert method.call_count == 2 - method.assert_called_with(timeout=60, metadata=mock.ANY) + method.assert_called_with( + timeout=60, compression=default_compression, metadata=mock.ANY + ) @mock.patch("time.sleep") @@ -145,37 +148,45 @@ def test_wrap_method_with_default_retry_and_timeout_using_sentinel(unused_sleep) ) default_retry = retry.Retry() default_timeout = timeout.ConstantTimeout(60) + default_compression = grpc.Compression.Gzip wrapped_method = google.api_core.gapic_v1.method.wrap_method( - method, default_retry, default_timeout + method, default_retry, default_timeout, default_compression ) result = wrapped_method( retry=google.api_core.gapic_v1.method.DEFAULT, timeout=google.api_core.gapic_v1.method.DEFAULT, + compression=google.api_core.gapic_v1.method.DEFAULT, ) assert result == 42 assert method.call_count == 2 - method.assert_called_with(timeout=60, metadata=mock.ANY) + method.assert_called_with( + timeout=60, compression=default_compression, metadata=mock.ANY + ) @mock.patch("time.sleep") -def test_wrap_method_with_overriding_retry_and_timeout(unused_sleep): +def test_wrap_method_with_overriding_retry_timeout_compression(unused_sleep): method = mock.Mock(spec=["__call__"], side_effect=[exceptions.NotFound(None), 42]) default_retry = retry.Retry() default_timeout = timeout.ConstantTimeout(60) + default_compression = grpc.Compression.Gzip wrapped_method = google.api_core.gapic_v1.method.wrap_method( - method, default_retry, default_timeout + method, default_retry, default_timeout, default_compression ) result = wrapped_method( retry=retry.Retry(retry.if_exception_type(exceptions.NotFound)), timeout=timeout.ConstantTimeout(22), + compression=grpc.Compression.Deflate, ) assert result == 42 assert method.call_count == 2 - method.assert_called_with(timeout=22, metadata=mock.ANY) + method.assert_called_with( + timeout=22, compression=grpc.Compression.Deflate, metadata=mock.ANY + ) def test_wrap_method_with_overriding_timeout_as_a_number(): diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index a99dc280..4eccbcaa 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -359,7 +359,7 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c target = "example.com:443" composite_creds = composite_creds_call.return_value - channel = grpc_helpers.create_channel(target) + channel = grpc_helpers.create_channel(target, compression=grpc.Compression.Gzip) assert channel is grpc_secure_channel.return_value @@ -368,7 +368,9 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=grpc.Compression.Gzip + ) @mock.patch("google.auth.transport.grpc.AuthMetadataPlugin", autospec=True) @@ -403,7 +405,9 @@ def test_create_channel_implicit_with_default_host( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -430,7 +434,9 @@ def test_create_channel_implicit_with_ssl_creds( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -455,7 +461,9 @@ def test_create_channel_implicit_with_scopes( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -480,7 +488,9 @@ def test_create_channel_implicit_with_default_scopes( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) def test_create_channel_explicit_with_duplicate_credentials(): @@ -512,7 +522,9 @@ def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_cred if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -536,7 +548,9 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -564,7 +578,9 @@ def test_create_channel_explicit_default_scopes( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -590,7 +606,9 @@ def test_create_channel_explicit_with_quota_project( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -619,7 +637,9 @@ def test_create_channel_with_credentials_file( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -651,7 +671,9 @@ def test_create_channel_with_credentials_file_and_scopes( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @mock.patch("grpc.composite_channel_credentials") @@ -683,7 +705,9 @@ def test_create_channel_with_credentials_file_and_default_scopes( if grpc_helpers.HAS_GRPC_GCP: # pragma: NO COVER grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: - grpc_secure_channel.assert_called_once_with(target, composite_creds) + grpc_secure_channel.assert_called_once_with( + target, composite_creds, compression=None + ) @pytest.mark.skipif( @@ -813,6 +837,7 @@ def test_call_info(self): stub = operations_pb2.OperationsStub(channel) expected_request = operations_pb2.GetOperationRequest(name="meep") expected_response = operations_pb2.Operation(name="moop") + expected_compression = grpc.Compression.NoCompression expected_metadata = [("red", "blue"), ("two", "shoe")] expected_credentials = mock.sentinel.credentials channel.GetOperation.response = expected_response @@ -820,6 +845,7 @@ def test_call_info(self): response = stub.GetOperation( expected_request, timeout=42, + compression=expected_compression, metadata=expected_metadata, credentials=expected_credentials, ) @@ -827,7 +853,13 @@ def test_call_info(self): assert response == expected_response assert channel.requests == [("GetOperation", expected_request)] assert channel.GetOperation.calls == [ - (expected_request, 42, expected_metadata, expected_credentials) + ( + expected_request, + 42, + expected_metadata, + expected_credentials, + expected_compression, + ) ] def test_unary_unary(self):