diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 108063d..b668c04 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -1,3 +1,17 @@ +# Copyright 2022 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. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:4ee57a76a176ede9087c14330c625a71553cf9c72828b2c0ca12f5338171ba60 + digest: sha256:ed1f9983d5a935a89fe8085e8bb97d94e41015252c5b6c9771257cf8624367e6 + diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 30c3973..62aced9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,9 +3,10 @@ # # For syntax help see: # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax +# Note: This file is autogenerated. To make changes to the codeowner team, please update .repo-metadata.json. -# The @googleapis/yoshi-python is the default owner for changes in this repo -* @googleapis/yoshi-python +# @googleapis/yoshi-python @googleapis/cdpe-cloudai are the default owners for changes in this repo +* @googleapis/yoshi-python @googleapis/cdpe-cloudai -# The python-samples-reviewers team is the default owner for samples changes -/samples/ @googleapis/python-samples-owners \ No newline at end of file +# @googleapis/python-samples-reviewers @googleapis/cdpe-cloudai are the default owners for samples changes +/samples/ @googleapis/python-samples-reviewers @googleapis/cdpe-cloudai diff --git a/.github/release-please.yml b/.github/release-please.yml index 4507ad0..466597e 100644 --- a/.github/release-please.yml +++ b/.github/release-please.yml @@ -1 +1,2 @@ releaseType: python +handleGHRelease: true diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml new file mode 100644 index 0000000..d4ca941 --- /dev/null +++ b/.github/release-trigger.yml @@ -0,0 +1 @@ +enabled: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..f7b8344 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,38 @@ +on: + pull_request: + branches: + - main +name: docs +jobs: + docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run docs + run: | + nox -s docs + docfx: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run docfx + run: | + nox -s docfx diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..1e8b05c --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,25 @@ +on: + pull_request: + branches: + - main +name: lint +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run lint + run: | + nox -s lint + - name: Run lint_setup_py + run: | + nox -s lint_setup_py diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml new file mode 100644 index 0000000..074ee25 --- /dev/null +++ b/.github/workflows/unittest.yml @@ -0,0 +1,57 @@ +on: + pull_request: + branches: + - main +name: unittest +jobs: + unit: + runs-on: ubuntu-latest + strategy: + matrix: + python: ['3.6', '3.7', '3.8', '3.9', '3.10'] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run unit tests + env: + COVERAGE_FILE: .coverage-${{ matrix.python }} + run: | + nox -s unit-${{ matrix.python }} + - name: Upload coverage results + uses: actions/upload-artifact@v2 + with: + name: coverage-artifacts + path: .coverage-${{ matrix.python }} + + cover: + runs-on: ubuntu-latest + needs: + - unit + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install coverage + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install coverage + - name: Download coverage results + uses: actions/download-artifact@v2 + with: + name: coverage-artifacts + path: .coverage-results/ + - name: Report coverage results + run: | + coverage combine .coverage-results/.coverage* + coverage report --show-missing --fail-under=100 diff --git a/.kokoro/release.sh b/.kokoro/release.sh index 6c2f383..1fee706 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -26,7 +26,7 @@ python3 -m pip install --upgrade twine wheel setuptools export PYTHONUNBUFFERED=1 # Move into the package, build the distribution and upload. -TWINE_PASSWORD=$(cat "${KOKORO_GFILE_DIR}/secret_manager/google-cloud-pypi-token") +TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google-cloud-pypi-token-keystore-1") cd github/python-media-translation python3 setup.py sdist bdist_wheel twine upload --username __token__ --password "${TWINE_PASSWORD}" dist/* diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index cb212de..515be2b 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -23,8 +23,18 @@ env_vars: { value: "github/python-media-translation/.kokoro/release.sh" } +# Fetch PyPI password +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "google-cloud-pypi-token-keystore-1" + } + } +} + # Tokens needed to report release status back to GitHub env_vars: { key: "SECRET_MANAGER_KEYS" - value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem,google-cloud-pypi-token" + value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" } diff --git a/.repo-metadata.json b/.repo-metadata.json index b0769cc..be93e60 100644 --- a/.repo-metadata.json +++ b/.repo-metadata.json @@ -2,14 +2,15 @@ "name": "mediatranslation", "name_pretty": "Media Translation", "product_documentation": "https://cloud.google.com/media-translation", - "client_documentation": "https://googleapis.dev/python/mediatranslation/latest", + "client_documentation": "https://cloud.google.com/python/docs/reference/mediatranslation/latest", "issue_tracker": "", - "release_level": "beta", + "release_level": "preview", "language": "python", "library_type": "GAPIC_AUTO", "repo": "googleapis/python-media-translation", "distribution_name": "google-cloud-media-translation", "api_id": "mediatranslation.googleapis.com", "default_version": "v1beta1", - "codeowner_team": "" + "codeowner_team": "@googleapis/cdpe-cloudai", + "api_shortname": "mediatranslation" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 07a4e9b..4389a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [0.8.0](https://github.com/googleapis/python-media-translation/compare/v0.7.1...v0.8.0) (2022-02-26) + + +### Features + +* add api key support ([#149](https://github.com/googleapis/python-media-translation/issues/149)) ([c1210da](https://github.com/googleapis/python-media-translation/commit/c1210da198758da06697df99e614b7bd1b2d6e7f)) + + +### Bug Fixes + +* resolve DuplicateCredentialArgs error when using credentials_file ([55a75a9](https://github.com/googleapis/python-media-translation/commit/55a75a96d8880a5e1a45934e828cef526ba9c423)) + + +### Documentation + +* add generated snippets ([#155](https://github.com/googleapis/python-media-translation/issues/155)) ([17cc6bb](https://github.com/googleapis/python-media-translation/commit/17cc6bb8e82f16486b1520bceb0bf6a1b438f6ae)) + ### [0.7.1](https://www.github.com/googleapis/python-media-translation/compare/v0.7.0...v0.7.1) (2021-11-01) diff --git a/README.rst b/README.rst index 8e5929b..6cf5975 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,7 @@ Python Client for Cloud Media Translation .. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-media-translation.svg :target: https://pypi.org/project/google-cloud-media-translation/ .. _Cloud Media Translation API: https://cloud.google.com/translate/media/docs -.. _Client Library Documentation: https://googleapis.dev/python/mediatranslation/latest +.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/mediatranslation/latest .. _Product Documentation: https://cloud.google.com/media-translation/ Quick Start diff --git a/google/cloud/mediatranslation/__init__.py b/google/cloud/mediatranslation/__init__.py index f8c78a1..91ea867 100644 --- a/google/cloud/mediatranslation/__init__.py +++ b/google/cloud/mediatranslation/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/cloud/mediatranslation_v1beta1/__init__.py b/google/cloud/mediatranslation_v1beta1/__init__.py index 0b82590..4414c21 100644 --- a/google/cloud/mediatranslation_v1beta1/__init__.py +++ b/google/cloud/mediatranslation_v1beta1/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/cloud/mediatranslation_v1beta1/services/__init__.py b/google/cloud/mediatranslation_v1beta1/services/__init__.py index 4de6597..e8e1c38 100644 --- a/google/cloud/mediatranslation_v1beta1/services/__init__.py +++ b/google/cloud/mediatranslation_v1beta1/services/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/cloud/mediatranslation_v1beta1/services/speech_translation_service/__init__.py b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/__init__.py index d2a8409..1383003 100644 --- a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/__init__.py +++ b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/cloud/mediatranslation_v1beta1/services/speech_translation_service/async_client.py b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/async_client.py index ff1ae3f..d995e28 100644 --- a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/async_client.py +++ b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/async_client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import re from typing import ( Dict, + Optional, AsyncIterable, Awaitable, AsyncIterator, @@ -28,14 +29,17 @@ ) import pkg_resources -from google.api_core.client_options import ClientOptions # type: ignore -from google.api_core import exceptions as core_exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore +from google.api_core.client_options import ClientOptions +from google.api_core import exceptions as core_exceptions +from google.api_core import gapic_v1 +from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore -OptionalRetry = Union[retries.Retry, object] +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object] # type: ignore from google.cloud.mediatranslation_v1beta1.types import media_translation from google.rpc import status_pb2 # type: ignore @@ -114,6 +118,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @classmethod + def get_mtls_endpoint_and_cert_source( + cls, client_options: Optional[ClientOptions] = None + ): + """Return the API endpoint and client cert source for mutual TLS. + + The client cert source is determined in the following order: + (1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the + client cert source is None. + (2) if `client_options.client_cert_source` is provided, use the provided one; if the + default client cert source exists, use the default one; otherwise the client cert + source is None. + + The API endpoint is determined in the following order: + (1) if `client_options.api_endpoint` if provided, use the provided one. + (2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the + default mTLS endpoint; if the environment variabel is "never", use the default API + endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise + use the default API endpoint. + + More details can be found at https://google.aip.dev/auth/4114. + + Args: + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. Only the `api_endpoint` and `client_cert_source` properties may be used + in this method. + + Returns: + Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the + client cert source to use. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If any errors happen. + """ + return SpeechTranslationServiceClient.get_mtls_endpoint_and_cert_source(client_options) # type: ignore + @property def transport(self) -> SpeechTranslationServiceTransport: """Returns the transport used by the client instance. @@ -189,6 +229,42 @@ def streaming_translate_speech( receive results while sending audio. This method is only available via the gRPC API (not REST). + + .. code-block:: python + + from google.cloud import mediatranslation_v1beta1 + + def sample_streaming_translate_speech(): + # Create a client + client = mediatranslation_v1beta1.SpeechTranslationServiceClient() + + # Initialize request argument(s) + streaming_config = mediatranslation_v1beta1.StreamingTranslateSpeechConfig() + streaming_config.audio_config.audio_encoding = "audio_encoding_value" + streaming_config.audio_config.source_language_code = "source_language_code_value" + streaming_config.audio_config.target_language_code = "target_language_code_value" + + request = mediatranslation_v1beta1.StreamingTranslateSpeechRequest( + streaming_config=streaming_config, + ) + + # This method expects an iterator which contains + # 'mediatranslation_v1beta1.StreamingTranslateSpeechRequest' objects + # Here we create a generator that yields a single `request` for + # demonstrative purposes. + requests = [request] + + def request_generator(): + for request in requests: + yield request + + # Make the request + stream = client.streaming_translate_speech(requests=request_generator()) + + # Handle the response + for response in stream: + print(response) + Args: requests (AsyncIterator[`google.cloud.mediatranslation_v1beta1.types.StreamingTranslateSpeechRequest`]): The request object AsyncIterator. The top-level message sent by the diff --git a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/client.py b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/client.py index 7e899a5..2f128fd 100644 --- a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/client.py +++ b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,23 +14,25 @@ # limitations under the License. # from collections import OrderedDict -from distutils import util import os import re from typing import Dict, Optional, Iterable, Iterator, Sequence, Tuple, Type, Union import pkg_resources -from google.api_core import client_options as client_options_lib # type: ignore -from google.api_core import exceptions as core_exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore +from google.api_core import client_options as client_options_lib +from google.api_core import exceptions as core_exceptions +from google.api_core import gapic_v1 +from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore -OptionalRetry = Union[retries.Retry, object] +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object] # type: ignore from google.cloud.mediatranslation_v1beta1.types import media_translation from google.rpc import status_pb2 # type: ignore @@ -218,6 +220,73 @@ def parse_common_location_path(path: str) -> Dict[str, str]: m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) return m.groupdict() if m else {} + @classmethod + def get_mtls_endpoint_and_cert_source( + cls, client_options: Optional[client_options_lib.ClientOptions] = None + ): + """Return the API endpoint and client cert source for mutual TLS. + + The client cert source is determined in the following order: + (1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the + client cert source is None. + (2) if `client_options.client_cert_source` is provided, use the provided one; if the + default client cert source exists, use the default one; otherwise the client cert + source is None. + + The API endpoint is determined in the following order: + (1) if `client_options.api_endpoint` if provided, use the provided one. + (2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the + default mTLS endpoint; if the environment variabel is "never", use the default API + endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise + use the default API endpoint. + + More details can be found at https://google.aip.dev/auth/4114. + + Args: + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. Only the `api_endpoint` and `client_cert_source` properties may be used + in this method. + + Returns: + Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the + client cert source to use. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If any errors happen. + """ + if client_options is None: + client_options = client_options_lib.ClientOptions() + use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") + use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_client_cert not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + if use_mtls_endpoint not in ("auto", "never", "always"): + raise MutualTLSChannelError( + "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + + # Figure out the client cert source to use. + client_cert_source = None + if use_client_cert == "true": + if client_options.client_cert_source: + client_cert_source = client_options.client_cert_source + elif mtls.has_default_client_cert_source(): + client_cert_source = mtls.default_client_cert_source() + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + elif use_mtls_endpoint == "always" or ( + use_mtls_endpoint == "auto" and client_cert_source + ): + api_endpoint = cls.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = cls.DEFAULT_ENDPOINT + + return api_endpoint, client_cert_source + def __init__( self, *, @@ -268,50 +337,22 @@ def __init__( if client_options is None: client_options = client_options_lib.ClientOptions() - # Create SSL credentials for mutual TLS if needed. - use_client_cert = bool( - util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source( + client_options ) - client_cert_source_func = None - is_mtls = False - if use_client_cert: - if client_options.client_cert_source: - is_mtls = True - client_cert_source_func = client_options.client_cert_source - else: - is_mtls = mtls.has_default_client_cert_source() - if is_mtls: - client_cert_source_func = mtls.default_client_cert_source() - else: - client_cert_source_func = None - - # Figure out which api endpoint to use. - if client_options.api_endpoint is not None: - api_endpoint = client_options.api_endpoint - else: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") - if use_mtls_env == "never": - api_endpoint = self.DEFAULT_ENDPOINT - elif use_mtls_env == "always": - api_endpoint = self.DEFAULT_MTLS_ENDPOINT - elif use_mtls_env == "auto": - if is_mtls: - api_endpoint = self.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = self.DEFAULT_ENDPOINT - else: - raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted " - "values: never, auto, always" - ) + api_key_value = getattr(client_options, "api_key", None) + if api_key_value and credentials: + raise ValueError( + "client_options.api_key and credentials are mutually exclusive" + ) # Save or instantiate the transport. # Ordinarily, we provide the transport, but allowing a custom transport # instance provides an extensibility point for unusual situations. if isinstance(transport, SpeechTranslationServiceTransport): # transport is a SpeechTranslationServiceTransport instance. - if credentials or client_options.credentials_file: + if credentials or client_options.credentials_file or api_key_value: raise ValueError( "When providing a transport instance, " "provide its credentials directly." @@ -323,6 +364,15 @@ def __init__( ) self._transport = transport else: + import google.auth._default # type: ignore + + if api_key_value and hasattr( + google.auth._default, "get_api_key_credentials" + ): + credentials = google.auth._default.get_api_key_credentials( + api_key_value + ) + Transport = type(self).get_transport_class(transport) self._transport = Transport( credentials=credentials, @@ -347,6 +397,42 @@ def streaming_translate_speech( receive results while sending audio. This method is only available via the gRPC API (not REST). + + .. code-block:: python + + from google.cloud import mediatranslation_v1beta1 + + def sample_streaming_translate_speech(): + # Create a client + client = mediatranslation_v1beta1.SpeechTranslationServiceClient() + + # Initialize request argument(s) + streaming_config = mediatranslation_v1beta1.StreamingTranslateSpeechConfig() + streaming_config.audio_config.audio_encoding = "audio_encoding_value" + streaming_config.audio_config.source_language_code = "source_language_code_value" + streaming_config.audio_config.target_language_code = "target_language_code_value" + + request = mediatranslation_v1beta1.StreamingTranslateSpeechRequest( + streaming_config=streaming_config, + ) + + # This method expects an iterator which contains + # 'mediatranslation_v1beta1.StreamingTranslateSpeechRequest' objects + # Here we create a generator that yields a single `request` for + # demonstrative purposes. + requests = [request] + + def request_generator(): + for request in requests: + yield request + + # Make the request + stream = client.streaming_translate_speech(requests=request_generator()) + + # Handle the response + for response in stream: + print(response) + Args: requests (Iterator[google.cloud.mediatranslation_v1beta1.types.StreamingTranslateSpeechRequest]): The request object iterator. The top-level message sent by the diff --git a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/__init__.py b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/__init__.py index 187de33..136569a 100644 --- a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/__init__.py +++ b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/base.py b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/base.py index bb66fa8..b559904 100644 --- a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/base.py +++ b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ import pkg_resources import google.auth # type: ignore -import google.api_core # type: ignore -from google.api_core import exceptions as core_exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore +import google.api_core +from google.api_core import exceptions as core_exceptions +from google.api_core import gapic_v1 +from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore @@ -101,7 +101,6 @@ def __init__( credentials, _ = google.auth.load_credentials_from_file( credentials_file, **scopes_kwargs, quota_project_id=quota_project_id ) - elif credentials is None: credentials, _ = google.auth.default( **scopes_kwargs, quota_project_id=quota_project_id diff --git a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/grpc.py b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/grpc.py index fd3aafc..cf39717 100644 --- a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/grpc.py +++ b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/grpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ import warnings from typing import Callable, Dict, Optional, Sequence, Tuple, Union -from google.api_core import grpc_helpers # type: ignore -from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers +from google.api_core import gapic_v1 import google.auth # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore @@ -158,8 +158,11 @@ def __init__( if not self._grpc_channel: self._grpc_channel = type(self).create_channel( self._host, + # use the credentials which are saved credentials=self._credentials, - credentials_file=credentials_file, + # Set ``credentials_file`` to ``None`` here as + # the credentials that we saved earlier should be used. + credentials_file=None, scopes=self._scopes, ssl_credentials=self._ssl_channel_credentials, quota_project_id=quota_project_id, diff --git a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/grpc_asyncio.py b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/grpc_asyncio.py index f8aadad..224bd55 100644 --- a/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/grpc_asyncio.py +++ b/google/cloud/mediatranslation_v1beta1/services/speech_translation_service/transports/grpc_asyncio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union -from google.api_core import gapic_v1 # type: ignore -from google.api_core import grpc_helpers_async # type: ignore +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers_async from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore @@ -203,8 +203,11 @@ def __init__( if not self._grpc_channel: self._grpc_channel = type(self).create_channel( self._host, + # use the credentials which are saved credentials=self._credentials, - credentials_file=credentials_file, + # Set ``credentials_file`` to ``None`` here as + # the credentials that we saved earlier should be used. + credentials_file=None, scopes=self._scopes, ssl_credentials=self._ssl_channel_credentials, quota_project_id=quota_project_id, diff --git a/google/cloud/mediatranslation_v1beta1/types/__init__.py b/google/cloud/mediatranslation_v1beta1/types/__init__.py index d782b7b..060012f 100644 --- a/google/cloud/mediatranslation_v1beta1/types/__init__.py +++ b/google/cloud/mediatranslation_v1beta1/types/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/cloud/mediatranslation_v1beta1/types/media_translation.py b/google/cloud/mediatranslation_v1beta1/types/media_translation.py index d93b65f..a6bdb5b 100644 --- a/google/cloud/mediatranslation_v1beta1/types/media_translation.py +++ b/google/cloud/mediatranslation_v1beta1/types/media_translation.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -162,6 +162,7 @@ class StreamingTranslateSpeechRequest(proto.Message): process the request. The first ``StreamingTranslateSpeechRequest`` message must contain a ``streaming_config`` message. + This field is a member of `oneof`_ ``streaming_request``. audio_content (bytes): The audio data to be translated. Sequential chunks of audio @@ -174,6 +175,7 @@ class StreamingTranslateSpeechRequest(proto.Message): specified in ``StreamingTranslateSpeechConfig``. Note: as with all bytes fields, protobuffers use a pure binary representation (not base64). + This field is a member of `oneof`_ ``streaming_request``. """ @@ -196,6 +198,7 @@ class StreamingTranslateSpeechResult(proto.Message): Attributes: text_translation_result (google.cloud.mediatranslation_v1beta1.types.StreamingTranslateSpeechResult.TextTranslationResult): Text translation result. + This field is a member of `oneof`_ ``result``. """ diff --git a/samples/generated_samples/mediatranslation_v1beta1_generated_speech_translation_service_streaming_translate_speech_async.py b/samples/generated_samples/mediatranslation_v1beta1_generated_speech_translation_service_streaming_translate_speech_async.py new file mode 100644 index 0000000..961aa78 --- /dev/null +++ b/samples/generated_samples/mediatranslation_v1beta1_generated_speech_translation_service_streaming_translate_speech_async.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 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. +# +# Generated code. DO NOT EDIT! +# +# Snippet for StreamingTranslateSpeech +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-media-translation + + +# [START mediatranslation_v1beta1_generated_SpeechTranslationService_StreamingTranslateSpeech_async] +from google.cloud import mediatranslation_v1beta1 + + +async def sample_streaming_translate_speech(): + # Create a client + client = mediatranslation_v1beta1.SpeechTranslationServiceAsyncClient() + + # Initialize request argument(s) + streaming_config = mediatranslation_v1beta1.StreamingTranslateSpeechConfig() + streaming_config.audio_config.audio_encoding = "audio_encoding_value" + streaming_config.audio_config.source_language_code = "source_language_code_value" + streaming_config.audio_config.target_language_code = "target_language_code_value" + + request = mediatranslation_v1beta1.StreamingTranslateSpeechRequest( + streaming_config=streaming_config, + ) + + # This method expects an iterator which contains + # 'mediatranslation_v1beta1.StreamingTranslateSpeechRequest' objects + # Here we create a generator that yields a single `request` for + # demonstrative purposes. + requests = [request] + + def request_generator(): + for request in requests: + yield request + + # Make the request + stream = await client.streaming_translate_speech(requests=request_generator()) + + # Handle the response + async for response in stream: + print(response) + +# [END mediatranslation_v1beta1_generated_SpeechTranslationService_StreamingTranslateSpeech_async] diff --git a/samples/generated_samples/mediatranslation_v1beta1_generated_speech_translation_service_streaming_translate_speech_sync.py b/samples/generated_samples/mediatranslation_v1beta1_generated_speech_translation_service_streaming_translate_speech_sync.py new file mode 100644 index 0000000..6419705 --- /dev/null +++ b/samples/generated_samples/mediatranslation_v1beta1_generated_speech_translation_service_streaming_translate_speech_sync.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 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. +# +# Generated code. DO NOT EDIT! +# +# Snippet for StreamingTranslateSpeech +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-media-translation + + +# [START mediatranslation_v1beta1_generated_SpeechTranslationService_StreamingTranslateSpeech_sync] +from google.cloud import mediatranslation_v1beta1 + + +def sample_streaming_translate_speech(): + # Create a client + client = mediatranslation_v1beta1.SpeechTranslationServiceClient() + + # Initialize request argument(s) + streaming_config = mediatranslation_v1beta1.StreamingTranslateSpeechConfig() + streaming_config.audio_config.audio_encoding = "audio_encoding_value" + streaming_config.audio_config.source_language_code = "source_language_code_value" + streaming_config.audio_config.target_language_code = "target_language_code_value" + + request = mediatranslation_v1beta1.StreamingTranslateSpeechRequest( + streaming_config=streaming_config, + ) + + # This method expects an iterator which contains + # 'mediatranslation_v1beta1.StreamingTranslateSpeechRequest' objects + # Here we create a generator that yields a single `request` for + # demonstrative purposes. + requests = [request] + + def request_generator(): + for request in requests: + yield request + + # Make the request + stream = client.streaming_translate_speech(requests=request_generator()) + + # Handle the response + for response in stream: + print(response) + +# [END mediatranslation_v1beta1_generated_SpeechTranslationService_StreamingTranslateSpeech_sync] diff --git a/samples/generated_samples/snippet_metadata_mediatranslation_v1beta1.json b/samples/generated_samples/snippet_metadata_mediatranslation_v1beta1.json new file mode 100644 index 0000000..dc0c00a --- /dev/null +++ b/samples/generated_samples/snippet_metadata_mediatranslation_v1beta1.json @@ -0,0 +1,93 @@ +{ + "snippets": [ + { + "clientMethod": { + "async": true, + "method": { + "service": { + "shortName": "SpeechTranslationService" + }, + "shortName": "StreamingTranslateSpeech" + } + }, + "file": "mediatranslation_v1beta1_generated_speech_translation_service_streaming_translate_speech_async.py", + "regionTag": "mediatranslation_v1beta1_generated_SpeechTranslationService_StreamingTranslateSpeech_async", + "segments": [ + { + "end": 60, + "start": 27, + "type": "FULL" + }, + { + "end": 60, + "start": 27, + "type": "SHORT" + }, + { + "end": 33, + "start": 31, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 53, + "start": 34, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 56, + "start": 54, + "type": "REQUEST_EXECUTION" + }, + { + "end": 61, + "start": 57, + "type": "RESPONSE_HANDLING" + } + ] + }, + { + "clientMethod": { + "method": { + "service": { + "shortName": "SpeechTranslationService" + }, + "shortName": "StreamingTranslateSpeech" + } + }, + "file": "mediatranslation_v1beta1_generated_speech_translation_service_streaming_translate_speech_sync.py", + "regionTag": "mediatranslation_v1beta1_generated_SpeechTranslationService_StreamingTranslateSpeech_sync", + "segments": [ + { + "end": 60, + "start": 27, + "type": "FULL" + }, + { + "end": 60, + "start": 27, + "type": "SHORT" + }, + { + "end": 33, + "start": 31, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 53, + "start": 34, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 56, + "start": 54, + "type": "REQUEST_EXECUTION" + }, + { + "end": 61, + "start": 57, + "type": "RESPONSE_HANDLING" + } + ] + } + ] +} diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 93a9122..20cdfc6 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -14,6 +14,7 @@ from __future__ import print_function +import glob import os from pathlib import Path import sys @@ -184,37 +185,45 @@ def blacken(session: nox.sessions.Session) -> None: def _session_tests( session: nox.sessions.Session, post_install: Callable = None ) -> None: - if TEST_CONFIG["pip_version_override"]: - pip_version = TEST_CONFIG["pip_version_override"] - session.install(f"pip=={pip_version}") - """Runs py.test for a particular project.""" - if os.path.exists("requirements.txt"): - if os.path.exists("constraints.txt"): - session.install("-r", "requirements.txt", "-c", "constraints.txt") - else: - session.install("-r", "requirements.txt") - - if os.path.exists("requirements-test.txt"): - if os.path.exists("constraints-test.txt"): - session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt") - else: - session.install("-r", "requirements-test.txt") - - if INSTALL_LIBRARY_FROM_SOURCE: - session.install("-e", _get_repo_root()) - - if post_install: - post_install(session) - - session.run( - "pytest", - *(PYTEST_COMMON_ARGS + session.posargs), - # Pytest will return 5 when no tests are collected. This can happen - # on travis where slow and flaky tests are excluded. - # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html - success_codes=[0, 5], - env=get_pytest_env_vars(), - ) + # check for presence of tests + test_list = glob.glob("*_test.py") + glob.glob("test_*.py") + test_list.extend(glob.glob("tests")) + if len(test_list) == 0: + print("No tests found, skipping directory.") + else: + if TEST_CONFIG["pip_version_override"]: + pip_version = TEST_CONFIG["pip_version_override"] + session.install(f"pip=={pip_version}") + """Runs py.test for a particular project.""" + if os.path.exists("requirements.txt"): + if os.path.exists("constraints.txt"): + session.install("-r", "requirements.txt", "-c", "constraints.txt") + else: + session.install("-r", "requirements.txt") + + if os.path.exists("requirements-test.txt"): + if os.path.exists("constraints-test.txt"): + session.install( + "-r", "requirements-test.txt", "-c", "constraints-test.txt" + ) + else: + session.install("-r", "requirements-test.txt") + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs), + # Pytest will return 5 when no tests are collected. This can happen + # on travis where slow and flaky tests are excluded. + # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html + success_codes=[0, 5], + env=get_pytest_env_vars(), + ) @nox.session(python=ALL_VERSIONS) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 9270945..c2845bf 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1 +1 @@ -pytest==6.2.5 +pytest==7.0.1 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 5d9fe48..9f038af 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,3 @@ -google-cloud-media-translation==0.7.0 +google-cloud-media-translation==0.7.1 pyaudio==0.2.11 six==1.16.0 \ No newline at end of file diff --git a/samples/snippets/translate_from_file_test.py b/samples/snippets/translate_from_file_test.py index ce7a0ba..0abbe97 100644 --- a/samples/snippets/translate_from_file_test.py +++ b/samples/snippets/translate_from_file_test.py @@ -17,12 +17,11 @@ import translate_from_file -RESOURCES = os.path.join(os.path.dirname(__file__), 'resources') +RESOURCES = os.path.join(os.path.dirname(__file__), "resources") def test_translate_streaming(capsys): - translate_from_file.translate_from_file( - os.path.join(RESOURCES, 'audio.raw')) + translate_from_file.translate_from_file(os.path.join(RESOURCES, "audio.raw")) out, err = capsys.readouterr() - assert re.search(r'Partial translation', out, re.DOTALL | re.I) + assert re.search(r"Partial translation", out, re.DOTALL | re.I) diff --git a/samples/snippets/translate_from_mic.py b/samples/snippets/translate_from_mic.py index 3d2e616..5990404 100644 --- a/samples/snippets/translate_from_mic.py +++ b/samples/snippets/translate_from_mic.py @@ -48,8 +48,10 @@ def __enter__(self): self._audio_interface = pyaudio.PyAudio() self._audio_stream = self._audio_interface.open( format=pyaudio.paInt16, - channels=1, rate=self._rate, - input=True, frames_per_buffer=self._chunk, + channels=1, + rate=self._rate, + input=True, + frames_per_buffer=self._chunk, # Run the audio stream asynchronously to fill the buffer object. # This is necessary so that the input device's buffer doesn't # overflow while the calling thread makes network requests, etc. @@ -97,7 +99,7 @@ def generator(self): except queue.Empty: break - yield b''.join(data) + yield b"".join(data) def listen_print_loop(responses): @@ -106,45 +108,46 @@ def listen_print_loop(responses): The responses passed is a generator that will block until a response is provided by the server. """ - translation = '' + translation = "" for response in responses: # Once the transcription settles, the response contains the # END_OF_SINGLE_UTTERANCE event. - if (response.speech_event_type == - SpeechEventType.END_OF_SINGLE_UTTERANCE): + if response.speech_event_type == SpeechEventType.END_OF_SINGLE_UTTERANCE: - print(u'\nFinal translation: {0}'.format(translation)) + print(u"\nFinal translation: {0}".format(translation)) return 0 result = response.result translation = result.text_translation_result.translation - print(u'\nPartial translation: {0}'.format(translation)) + print(u"\nPartial translation: {0}".format(translation)) def do_translation_loop(): - print('Begin speaking...') + print("Begin speaking...") client = media.SpeechTranslationServiceClient() speech_config = media.TranslateSpeechConfig( - audio_encoding='linear16', - source_language_code='en-US', - target_language_code='es-ES') + audio_encoding="linear16", + source_language_code="en-US", + target_language_code="es-ES", + ) config = media.StreamingTranslateSpeechConfig( - audio_config=speech_config, single_utterance=True) + audio_config=speech_config, single_utterance=True + ) # The first request contains the configuration. # Note that audio_content is explicitly set to None. - first_request = media.StreamingTranslateSpeechRequest( - streaming_config=config) + first_request = media.StreamingTranslateSpeechRequest(streaming_config=config) with MicrophoneStream(RATE, CHUNK) as stream: audio_generator = stream.generator() - mic_requests = (media.StreamingTranslateSpeechRequest( - audio_content=content) - for content in audio_generator) + mic_requests = ( + media.StreamingTranslateSpeechRequest(audio_content=content) + for content in audio_generator + ) requests = itertools.chain(iter([first_request]), mic_requests) @@ -159,14 +162,14 @@ def do_translation_loop(): def main(): while True: print() - option = input('Press any key to translate or \'q\' to quit: ') + option = input("Press any key to translate or 'q' to quit: ") - if option.lower() == 'q': + if option.lower() == "q": break do_translation_loop() -if __name__ == '__main__': +if __name__ == "__main__": main() # [END mediatranslation_translate_from_mic] diff --git a/scripts/fixup_mediatranslation_v1beta1_keywords.py b/scripts/fixup_mediatranslation_v1beta1_keywords.py index 809ded8..95650ce 100644 --- a/scripts/fixup_mediatranslation_v1beta1_keywords.py +++ b/scripts/fixup_mediatranslation_v1beta1_keywords.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/setup.py b/setup.py index 49bc77e..10f6c3b 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ import os import setuptools # type: ignore -version = "0.7.1" +version = "0.8.0" package_root = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/__init__.py b/tests/__init__.py index 4de6597..e8e1c38 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/tests/unit/__init__.py b/tests/unit/__init__.py index 4de6597..e8e1c38 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/tests/unit/gapic/__init__.py b/tests/unit/gapic/__init__.py index 4de6597..e8e1c38 100644 --- a/tests/unit/gapic/__init__.py +++ b/tests/unit/gapic/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/tests/unit/gapic/mediatranslation_v1beta1/__init__.py b/tests/unit/gapic/mediatranslation_v1beta1/__init__.py index 4de6597..e8e1c38 100644 --- a/tests/unit/gapic/mediatranslation_v1beta1/__init__.py +++ b/tests/unit/gapic/mediatranslation_v1beta1/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 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/tests/unit/gapic/mediatranslation_v1beta1/test_speech_translation_service.py b/tests/unit/gapic/mediatranslation_v1beta1/test_speech_translation_service.py index cf3b83b..0693758 100644 --- a/tests/unit/gapic/mediatranslation_v1beta1/test_speech_translation_service.py +++ b/tests/unit/gapic/mediatranslation_v1beta1/test_speech_translation_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -263,20 +263,20 @@ def test_speech_translation_service_client_client_options( # unsupported value. with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): with pytest.raises(MutualTLSChannelError): - client = client_class() + client = client_class(transport=transport_name) # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} ): with pytest.raises(ValueError): - client = client_class() + client = client_class(transport=transport_name) # Check the case quota_project_id is provided options = client_options.ClientOptions(quota_project_id="octopus") with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None - client = client_class(transport=transport_name, client_options=options) + client = client_class(client_options=options, transport=transport_name) patched.assert_called_once_with( credentials=None, credentials_file=None, @@ -345,7 +345,7 @@ def test_speech_translation_service_client_mtls_env_auto( ) with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None - client = client_class(transport=transport_name, client_options=options) + client = client_class(client_options=options, transport=transport_name) if use_client_cert_env == "false": expected_client_cert_source = None @@ -422,6 +422,90 @@ def test_speech_translation_service_client_mtls_env_auto( ) +@pytest.mark.parametrize( + "client_class", + [SpeechTranslationServiceClient, SpeechTranslationServiceAsyncClient], +) +@mock.patch.object( + SpeechTranslationServiceClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SpeechTranslationServiceClient), +) +@mock.patch.object( + SpeechTranslationServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SpeechTranslationServiceAsyncClient), +) +def test_speech_translation_service_client_get_mtls_endpoint_and_cert_source( + client_class, +): + mock_client_cert_source = mock.Mock() + + # Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "true". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint + ) + api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source( + options + ) + assert api_endpoint == mock_api_endpoint + assert cert_source == mock_client_cert_source + + # Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "false". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}): + mock_client_cert_source = mock.Mock() + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint + ) + api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source( + options + ) + assert api_endpoint == mock_api_endpoint + assert cert_source is None + + # Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source() + assert api_endpoint == client_class.DEFAULT_ENDPOINT + assert cert_source is None + + # Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source() + assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT + assert cert_source is None + + # Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert doesn't exist. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source() + assert api_endpoint == client_class.DEFAULT_ENDPOINT + assert cert_source is None + + # Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert exists. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=mock_client_cert_source, + ): + ( + api_endpoint, + cert_source, + ) = client_class.get_mtls_endpoint_and_cert_source() + assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT + assert cert_source == mock_client_cert_source + + @pytest.mark.parametrize( "client_class,transport_class,transport_name", [ @@ -444,7 +528,7 @@ def test_speech_translation_service_client_client_options_scopes( options = client_options.ClientOptions(scopes=["1", "2"],) with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None - client = client_class(transport=transport_name, client_options=options) + client = client_class(client_options=options, transport=transport_name) patched.assert_called_once_with( credentials=None, credentials_file=None, @@ -458,28 +542,31 @@ def test_speech_translation_service_client_client_options_scopes( @pytest.mark.parametrize( - "client_class,transport_class,transport_name", + "client_class,transport_class,transport_name,grpc_helpers", [ ( SpeechTranslationServiceClient, transports.SpeechTranslationServiceGrpcTransport, "grpc", + grpc_helpers, ), ( SpeechTranslationServiceAsyncClient, transports.SpeechTranslationServiceGrpcAsyncIOTransport, "grpc_asyncio", + grpc_helpers_async, ), ], ) def test_speech_translation_service_client_client_options_credentials_file( - client_class, transport_class, transport_name + client_class, transport_class, transport_name, grpc_helpers ): # Check the case credentials file is provided. options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None - client = client_class(transport=transport_name, client_options=options) + client = client_class(client_options=options, transport=transport_name) patched.assert_called_once_with( credentials=None, credentials_file="credentials.json", @@ -512,10 +599,76 @@ def test_speech_translation_service_client_client_options_from_dict(): ) -def test_streaming_translate_speech( - transport: str = "grpc", - request_type=media_translation.StreamingTranslateSpeechRequest, +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,grpc_helpers", + [ + ( + SpeechTranslationServiceClient, + transports.SpeechTranslationServiceGrpcTransport, + "grpc", + grpc_helpers, + ), + ( + SpeechTranslationServiceAsyncClient, + transports.SpeechTranslationServiceGrpcAsyncIOTransport, + "grpc_asyncio", + grpc_helpers_async, + ), + ], +) +def test_speech_translation_service_client_create_channel_credentials_file( + client_class, transport_class, transport_name, grpc_helpers ): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options, transport=transport_name) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + always_use_jwt_access=True, + ) + + # test that the credentials from file are saved and used as the credentials. + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch.object( + google.auth, "default", autospec=True + ) as adc, mock.patch.object( + grpc_helpers, "create_channel" + ) as create_channel: + creds = ga_credentials.AnonymousCredentials() + file_creds = ga_credentials.AnonymousCredentials() + load_creds.return_value = (file_creds, None) + adc.return_value = (creds, None) + client = client_class(client_options=options, transport=transport_name) + create_channel.assert_called_with( + "mediatranslation.googleapis.com:443", + credentials=file_creds, + credentials_file=None, + quota_project_id=None, + default_scopes=("https://www.googleapis.com/auth/cloud-platform",), + scopes=None, + default_host="mediatranslation.googleapis.com", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + +@pytest.mark.parametrize( + "request_type", [media_translation.StreamingTranslateSpeechRequest, dict,] +) +def test_streaming_translate_speech(request_type, transport: str = "grpc"): client = SpeechTranslationServiceClient( credentials=ga_credentials.AnonymousCredentials(), transport=transport, ) @@ -543,10 +696,6 @@ def test_streaming_translate_speech( assert isinstance(message, media_translation.StreamingTranslateSpeechResponse) -def test_streaming_translate_speech_from_dict(): - test_streaming_translate_speech(request_type=dict) - - @pytest.mark.asyncio async def test_streaming_translate_speech_async( transport: str = "grpc_asyncio", @@ -607,6 +756,25 @@ def test_credentials_transport_error(): transport=transport, ) + # It is an error to provide an api_key and a transport instance. + transport = transports.SpeechTranslationServiceGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + options = client_options.ClientOptions() + options.api_key = "api_key" + with pytest.raises(ValueError): + client = SpeechTranslationServiceClient( + client_options=options, transport=transport, + ) + + # It is an error to provide an api_key and a credential. + options = mock.Mock() + options.api_key = "api_key" + with pytest.raises(ValueError): + client = SpeechTranslationServiceClient( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + # It is an error to provide scopes and a transport instance. transport = transports.SpeechTranslationServiceGrpcTransport( credentials=ga_credentials.AnonymousCredentials(), @@ -1083,7 +1251,7 @@ def test_parse_common_location_path(): assert expected == actual -def test_client_withDEFAULT_CLIENT_INFO(): +def test_client_with_default_client_info(): client_info = gapic_v1.client_info.ClientInfo() with mock.patch.object( @@ -1148,3 +1316,39 @@ def test_client_ctx(): with client: pass close.assert_called() + + +@pytest.mark.parametrize( + "client_class,transport_class", + [ + ( + SpeechTranslationServiceClient, + transports.SpeechTranslationServiceGrpcTransport, + ), + ( + SpeechTranslationServiceAsyncClient, + transports.SpeechTranslationServiceGrpcAsyncIOTransport, + ), + ], +) +def test_api_key_credentials(client_class, transport_class): + with mock.patch.object( + google.auth._default, "get_api_key_credentials", create=True + ) as get_api_key_credentials: + mock_cred = mock.Mock() + get_api_key_credentials.return_value = mock_cred + options = client_options.ClientOptions() + options.api_key = "api_key" + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=mock_cred, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + always_use_jwt_access=True, + )