From 8dc630a2c5524a63851160589ceb359fd133b071 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sun, 12 Jun 2022 11:00:04 -0400 Subject: [PATCH 01/11] chore: add prerelease nox session (#606) Source-Link: https://github.com/googleapis/synthtool/commit/050953d60f71b4ed4be563e032f03c192c50332f Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:65e656411895bff71cffcae97246966460160028f253c2e45b7a25d805a5b142 Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 +- .kokoro/continuous/prerelease-deps.cfg | 7 +++ .kokoro/presubmit/prerelease-deps.cfg | 7 +++ noxfile.py | 64 ++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 .kokoro/continuous/prerelease-deps.cfg create mode 100644 .kokoro/presubmit/prerelease-deps.cfg diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 757c9dca7..2185b5918 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:81ed5ecdfc7cac5b699ba4537376f3563f6f04122c4ec9e735d3b3dc1d43dd32 -# created: 2022-05-05T22:08:23.383410683Z + digest: sha256:65e656411895bff71cffcae97246966460160028f253c2e45b7a25d805a5b142 +# created: 2022-06-12T13:11:45.905884945Z diff --git a/.kokoro/continuous/prerelease-deps.cfg b/.kokoro/continuous/prerelease-deps.cfg new file mode 100644 index 000000000..3595fb43f --- /dev/null +++ b/.kokoro/continuous/prerelease-deps.cfg @@ -0,0 +1,7 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Only run this nox session. +env_vars: { + key: "NOX_SESSION" + value: "prerelease_deps" +} diff --git a/.kokoro/presubmit/prerelease-deps.cfg b/.kokoro/presubmit/prerelease-deps.cfg new file mode 100644 index 000000000..3595fb43f --- /dev/null +++ b/.kokoro/presubmit/prerelease-deps.cfg @@ -0,0 +1,7 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Only run this nox session. +env_vars: { + key: "NOX_SESSION" + value: "prerelease_deps" +} diff --git a/noxfile.py b/noxfile.py index 740697a02..cb022e423 100644 --- a/noxfile.py +++ b/noxfile.py @@ -363,3 +363,67 @@ def docfx(session): os.path.join("docs", ""), os.path.join("docs", "_build", "html", ""), ) + + +@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) +def prerelease_deps(session): + """Run all tests with prerelease versions of dependencies installed.""" + + prerel_deps = [ + "protobuf", + "googleapis-common-protos", + "google-auth", + "grpcio", + "grpcio-status", + "google-api-core", + "proto-plus", + # dependencies of google-auth + "cryptography", + "pyasn1", + ] + + for dep in prerel_deps: + session.install("--pre", "--no-deps", "--upgrade", dep) + + # Remaining dependencies + other_deps = ["requests"] + session.install(*other_deps) + + session.install(*UNIT_TEST_STANDARD_DEPENDENCIES) + session.install(*SYSTEM_TEST_STANDARD_DEPENDENCIES) + + # Because we test minimum dependency versions on the minimum Python + # version, the first version we test with in the unit tests sessions has a + # constraints file containing all dependencies and extras. + with open( + CURRENT_DIRECTORY + / "testing" + / f"constraints-{UNIT_TEST_PYTHON_VERSIONS[0]}.txt", + encoding="utf-8", + ) as constraints_file: + constraints_text = constraints_file.read() + + # Ignore leading whitespace and comment lines. + deps = [ + match.group(1) + for match in re.finditer( + r"^\s*(\S+)(?===\S+)", constraints_text, flags=re.MULTILINE + ) + ] + + # Don't overwrite prerelease packages. + deps = [dep for dep in deps if dep not in prerel_deps] + # We use --no-deps to ensure that pre-release versions aren't overwritten + # by the version ranges in setup.py. + session.install(*deps) + session.install("--no-deps", "-e", ".[all]") + + # Print out prerelease package versions + session.run( + "python", "-c", "import google.protobuf; print(google.protobuf.__version__)" + ) + session.run("python", "-c", "import grpc; print(grpc.__version__)") + + session.run("py.test", "tests/unit") + session.run("py.test", "tests/system") + session.run("py.test", "samples/snippets") From 6905cf9ab50b74fd4aa5bf6877eab6c5e489bd8d Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 06:16:09 -0400 Subject: [PATCH 02/11] chore(python): add missing import for prerelease testing (#607) Source-Link: https://github.com/googleapis/synthtool/commit/d2871d98e1e767d4ad49a557ff979236d64361a1 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:b2dc5f80edcf5d4486c39068c9fa11f7f851d9568eea4dcba130f994ea9b5e97 Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- noxfile.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 2185b5918..50b29ffd2 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:65e656411895bff71cffcae97246966460160028f253c2e45b7a25d805a5b142 -# created: 2022-06-12T13:11:45.905884945Z + digest: sha256:b2dc5f80edcf5d4486c39068c9fa11f7f851d9568eea4dcba130f994ea9b5e97 +# created: 2022-06-12T16:09:31.61859086Z diff --git a/noxfile.py b/noxfile.py index cb022e423..2b89e8705 100644 --- a/noxfile.py +++ b/noxfile.py @@ -19,6 +19,7 @@ from __future__ import absolute_import import os import pathlib +import re import shutil import warnings From 16fa5ad27ae786680769e2f673fa1af0eaedf544 Mon Sep 17 00:00:00 2001 From: Mariatta Wijaya Date: Tue, 21 Jun 2022 09:45:45 -0700 Subject: [PATCH 03/11] samples: Add BigTable delete samples (#590) * doc: Add BigTable delete samples - Add the deleteion snippets - Add test cases - Update the row_key_prefix value - Remove `_sample` from function names. - Refactor the snapshot match assertion. - Renamed column to column_family_obj Closes internal issue #213629071 Co-authored-by: Owl Bot --- samples/snippets/README.md | 6 +- samples/snippets/deletes/deletes_snippets.py | 122 +++++++ samples/snippets/deletes/deletes_test.py | 139 ++++++++ samples/snippets/deletes/noxfile.py | 312 ++++++++++++++++++ .../snippets/deletes/requirements-test.txt | 1 + samples/snippets/deletes/requirements.txt | 2 + .../snippets/deletes/snapshots/__init__.py | 0 .../deletes/snapshots/snap_deletes_test.py | 24 ++ 8 files changed, 603 insertions(+), 3 deletions(-) create mode 100644 samples/snippets/deletes/deletes_snippets.py create mode 100644 samples/snippets/deletes/deletes_test.py create mode 100644 samples/snippets/deletes/noxfile.py create mode 100644 samples/snippets/deletes/requirements-test.txt create mode 100644 samples/snippets/deletes/requirements.txt create mode 100644 samples/snippets/deletes/snapshots/__init__.py create mode 100644 samples/snippets/deletes/snapshots/snap_deletes_test.py diff --git a/samples/snippets/README.md b/samples/snippets/README.md index 134b24732..7c0dd4463 100644 --- a/samples/snippets/README.md +++ b/samples/snippets/README.md @@ -3,8 +3,8 @@ ## Python Samples for Cloud Bigtable -This directory contains samples for Cloud Bigtable, which may be used as a refererence for how to use this product. -Samples, quickstarts, and other documentation are available at cloud.google.com. +This directory contains samples for Cloud Bigtable, which may be used as a reference for how to use this product. +Samples, quickstarts, and other documentation are available at [cloud.google.com](https://cloud.google.com/bigtable). ### Snippets @@ -17,7 +17,7 @@ This folder contains snippets for Python Cloud Bigtable. ## Additional Information You can read the documentation for more details on API usage and use GitHub -to browse the source and [report issues][issues]. +to [browse the source](https://github.com/googleapis/python-bigtable) and [report issues][issues]. ### Contributing View the [contributing guidelines][contrib_guide], the [Python style guide][py_style] for more information. diff --git a/samples/snippets/deletes/deletes_snippets.py b/samples/snippets/deletes/deletes_snippets.py new file mode 100644 index 000000000..4e89189db --- /dev/null +++ b/samples/snippets/deletes/deletes_snippets.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python + +# 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. + + +from google.cloud import bigtable + +# Write your code here. + + +# [START bigtable_delete_from_column] +def delete_from_column(project_id, instance_id, table_id): + client = bigtable.Client(project=project_id, admin=True) + instance = client.instance(instance_id) + table = instance.table(table_id) + row = table.row("phone#4c410523#20190501") + row.delete_cell(column_family_id="cell_plan", column="data_plan_01gb") + row.commit() + + +# [END bigtable_delete_from_column] + +# [START bigtable_delete_from_column_family] +def delete_from_column_family(project_id, instance_id, table_id): + client = bigtable.Client(project=project_id, admin=True) + instance = client.instance(instance_id) + table = instance.table(table_id) + row = table.row("phone#4c410523#20190501") + row.delete_cells( + column_family_id="cell_plan", columns=["data_plan_01gb", "data_plan_05gb"] + ) + row.commit() + + +# [END bigtable_delete_from_column_family] + + +# [START bigtable_delete_from_row] +def delete_from_row(project_id, instance_id, table_id): + client = bigtable.Client(project=project_id, admin=True) + instance = client.instance(instance_id) + table = instance.table(table_id) + row = table.row("phone#4c410523#20190501") + row.delete() + row.commit() + + +# [END bigtable_delete_from_row] + +# [START bigtable_streaming_and_batching] +def streaming_and_batching(project_id, instance_id, table_id): + client = bigtable.Client(project=project_id, admin=True) + instance = client.instance(instance_id) + table = instance.table(table_id) + batcher = table.mutations_batcher(flush_count=2) + rows = table.read_rows() + for row in rows: + row = table.row(row.row_key) + row.delete_cell(column_family_id="cell_plan", column="data_plan_01gb") + + batcher.mutate_rows(rows) + + +# [END bigtable_streaming_and_batching] + +# [START bigtable_check_and_mutate] +def check_and_mutate(project_id, instance_id, table_id): + client = bigtable.Client(project=project_id, admin=True) + instance = client.instance(instance_id) + table = instance.table(table_id) + row = table.row("phone#4c410523#20190501") + row.delete_cell(column_family_id="cell_plan", column="data_plan_01gb") + row.delete_cell(column_family_id="cell_plan", column="data_plan_05gb") + row.commit() + + +# [END bigtable_check_and_mutate] + + +# [START bigtable_drop_row_range] +def drop_row_range(project_id, instance_id, table_id): + client = bigtable.Client(project=project_id, admin=True) + instance = client.instance(instance_id) + table = instance.table(table_id) + row_key_prefix = "phone#4c410523" + table.drop_by_prefix(row_key_prefix, timeout=200) + + +# [END bigtable_drop_row_range] + +# [START bigtable_delete_column_family] +def delete_column_family(project_id, instance_id, table_id): + client = bigtable.Client(project=project_id, admin=True) + instance = client.instance(instance_id) + table = instance.table(table_id) + column_family_id = "stats_summary" + column_family_obj = table.column_family(column_family_id) + column_family_obj.delete() + + +# [END bigtable_delete_column_family] + +# [START bigtable_delete_table] +def delete_table(project_id, instance_id, table_id): + client = bigtable.Client(project=project_id, admin=True) + instance = client.instance(instance_id) + table = instance.table(table_id) + table.delete() + + +# [END bigtable_delete_table] diff --git a/samples/snippets/deletes/deletes_test.py b/samples/snippets/deletes/deletes_test.py new file mode 100644 index 000000000..bf23daa59 --- /dev/null +++ b/samples/snippets/deletes/deletes_test.py @@ -0,0 +1,139 @@ +# Copyright 2020, Google LLC +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import datetime +import os +import time +import uuid + +from google.cloud import bigtable +import pytest + +import deletes_snippets + +PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"] +BIGTABLE_INSTANCE = os.environ["BIGTABLE_INSTANCE"] +TABLE_ID_PREFIX = "mobile-time-series-{}" + + +@pytest.fixture(scope="module", autouse=True) +def table_id(): + from google.cloud.bigtable.row_set import RowSet + + client = bigtable.Client(project=PROJECT, admin=True) + instance = client.instance(BIGTABLE_INSTANCE) + + table_id = TABLE_ID_PREFIX.format(str(uuid.uuid4())[:16]) + table = instance.table(table_id) + if table.exists(): + table.delete() + + table.create(column_families={"stats_summary": None, "cell_plan": None}) + + timestamp = datetime.datetime(2019, 5, 1) + timestamp_minus_hr = datetime.datetime(2019, 5, 1) - datetime.timedelta(hours=1) + + row_keys = [ + "phone#4c410523#20190501", + "phone#4c410523#20190502", + "phone#4c410523#20190505", + "phone#5c10102#20190501", + "phone#5c10102#20190502", + ] + + rows = [table.direct_row(row_key) for row_key in row_keys] + + rows[0].set_cell("stats_summary", "connected_cell", 1, timestamp) + rows[0].set_cell("stats_summary", "connected_wifi", 1, timestamp) + rows[0].set_cell("stats_summary", "os_build", "PQ2A.190405.003", timestamp) + rows[0].set_cell("cell_plan", "data_plan_01gb", "true", timestamp_minus_hr) + rows[0].set_cell("cell_plan", "data_plan_01gb", "false", timestamp) + rows[0].set_cell("cell_plan", "data_plan_05gb", "true", timestamp) + rows[1].set_cell("stats_summary", "connected_cell", 1, timestamp) + rows[1].set_cell("stats_summary", "connected_wifi", 1, timestamp) + rows[1].set_cell("stats_summary", "os_build", "PQ2A.190405.004", timestamp) + rows[1].set_cell("cell_plan", "data_plan_05gb", "true", timestamp) + rows[2].set_cell("stats_summary", "connected_cell", 0, timestamp) + rows[2].set_cell("stats_summary", "connected_wifi", 1, timestamp) + rows[2].set_cell("stats_summary", "os_build", "PQ2A.190406.000", timestamp) + rows[2].set_cell("cell_plan", "data_plan_05gb", "true", timestamp) + rows[3].set_cell("stats_summary", "connected_cell", 1, timestamp) + rows[3].set_cell("stats_summary", "connected_wifi", 1, timestamp) + rows[3].set_cell("stats_summary", "os_build", "PQ2A.190401.002", timestamp) + rows[3].set_cell("cell_plan", "data_plan_10gb", "true", timestamp) + rows[4].set_cell("stats_summary", "connected_cell", 1, timestamp) + rows[4].set_cell("stats_summary", "connected_wifi", 0, timestamp) + rows[4].set_cell("stats_summary", "os_build", "PQ2A.190406.000", timestamp) + rows[4].set_cell("cell_plan", "data_plan_10gb", "true", timestamp) + + table.mutate_rows(rows) + + # Ensure mutations have propagated. + row_set = RowSet() + + for row_key in row_keys: + row_set.add_row_key(row_key) + + fetched = list(table.read_rows(row_set=row_set)) + + while len(fetched) < len(rows): + time.sleep(5) + fetched = list(table.read_rows(row_set=row_set)) + + yield table_id + + +def assert_snapshot_match(capsys, snapshot): + out, _ = capsys.readouterr() + snapshot.assert_match(out) + + +def test_delete_from_column(capsys, snapshot, table_id): + deletes_snippets.delete_from_column(PROJECT, BIGTABLE_INSTANCE, table_id) + assert_snapshot_match(capsys, snapshot) + + +def test_delete_from_column_family(capsys, snapshot, table_id): + deletes_snippets.delete_from_column_family(PROJECT, BIGTABLE_INSTANCE, table_id) + assert_snapshot_match(capsys, snapshot) + + +def test_delete_from_row(capsys, snapshot, table_id): + deletes_snippets.delete_from_row(PROJECT, BIGTABLE_INSTANCE, table_id) + assert_snapshot_match(capsys, snapshot) + + +def test_streaming_and_batching(capsys, snapshot, table_id): + deletes_snippets.streaming_and_batching(PROJECT, BIGTABLE_INSTANCE, table_id) + assert_snapshot_match(capsys, snapshot) + + +def test_check_and_mutate(capsys, snapshot, table_id): + deletes_snippets.check_and_mutate(PROJECT, BIGTABLE_INSTANCE, table_id) + assert_snapshot_match(capsys, snapshot) + + +def test_drop_row_range(capsys, snapshot, table_id): + deletes_snippets.drop_row_range(PROJECT, BIGTABLE_INSTANCE, table_id) + assert_snapshot_match(capsys, snapshot) + + +def test_delete_column_family(capsys, snapshot, table_id): + deletes_snippets.delete_column_family(PROJECT, BIGTABLE_INSTANCE, table_id) + assert_snapshot_match(capsys, snapshot) + + +def test_delete_table(capsys, snapshot, table_id): + deletes_snippets.delete_table(PROJECT, BIGTABLE_INSTANCE, table_id) + assert_snapshot_match(capsys, snapshot) diff --git a/samples/snippets/deletes/noxfile.py b/samples/snippets/deletes/noxfile.py new file mode 100644 index 000000000..38bb0a572 --- /dev/null +++ b/samples/snippets/deletes/noxfile.py @@ -0,0 +1,312 @@ +# Copyright 2019 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. + +from __future__ import print_function + +import glob +import os +from pathlib import Path +import sys +from typing import Callable, Dict, List, Optional + +import nox + + +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING +# DO NOT EDIT THIS FILE EVER! +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING + +BLACK_VERSION = "black==22.3.0" +ISORT_VERSION = "isort==5.10.1" + +# Copy `noxfile_config.py` to your directory and modify it instead. + +# `TEST_CONFIG` dict is a configuration hook that allows users to +# modify the test configurations. The values here should be in sync +# with `noxfile_config.py`. Users will copy `noxfile_config.py` into +# their directory and modify it. + +TEST_CONFIG = { + # You can opt out from the test for specific Python versions. + "ignored_versions": [], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": False, + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} + + +try: + # Ensure we can import noxfile_config in the project's directory. + sys.path.append(".") + from noxfile_config import TEST_CONFIG_OVERRIDE +except ImportError as e: + print("No user noxfile_config found: detail: {}".format(e)) + TEST_CONFIG_OVERRIDE = {} + +# Update the TEST_CONFIG with the user supplied values. +TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) + + +def get_pytest_env_vars() -> Dict[str, str]: + """Returns a dict for pytest invocation.""" + ret = {} + + # Override the GCLOUD_PROJECT and the alias. + env_key = TEST_CONFIG["gcloud_project_env"] + # This should error out if not set. + ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key] + + # Apply user supplied envs. + ret.update(TEST_CONFIG["envs"]) + return ret + + +# DO NOT EDIT - automatically generated. +# All versions used to test samples. +ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] + +# Any default versions that should be ignored. +IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] + +TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) + +INSTALL_LIBRARY_FROM_SOURCE = os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False) in ( + "True", + "true", +) + +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + +# +# Style Checks +# + + +def _determine_local_import_names(start_dir: str) -> List[str]: + """Determines all import names that should be considered "local". + + This is used when running the linter to insure that import order is + properly checked. + """ + file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] + return [ + basename + for basename, extension in file_ext_pairs + if extension == ".py" + or os.path.isdir(os.path.join(start_dir, basename)) + and basename not in ("__pycache__") + ] + + +# Linting with flake8. +# +# We ignore the following rules: +# E203: whitespace before ‘:’ +# E266: too many leading ‘#’ for block comment +# E501: line too long +# I202: Additional newline in a section of imports +# +# We also need to specify the rules which are ignored by default: +# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121'] +FLAKE8_COMMON_ARGS = [ + "--show-source", + "--builtin=gettext", + "--max-complexity=20", + "--import-order-style=google", + "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--max-line-length=88", +] + + +@nox.session +def lint(session: nox.sessions.Session) -> None: + if not TEST_CONFIG["enforce_type_hints"]: + session.install("flake8", "flake8-import-order") + else: + session.install("flake8", "flake8-import-order", "flake8-annotations") + + local_names = _determine_local_import_names(".") + args = FLAKE8_COMMON_ARGS + [ + "--application-import-names", + ",".join(local_names), + ".", + ] + session.run("flake8", *args) + + +# +# Black +# + + +@nox.session +def blacken(session: nox.sessions.Session) -> None: + """Run black. Format code to uniform standard.""" + session.install(BLACK_VERSION) + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + session.run("black", *python_files) + + +# +# format = isort + black +# + +@nox.session +def format(session: nox.sessions.Session) -> None: + """ + Run isort to sort imports. Then run black + to format code to uniform standard. + """ + session.install(BLACK_VERSION, ISORT_VERSION) + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + # Use the --fss option to sort imports using strict alphabetical order. + # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections + session.run("isort", "--fss", *python_files) + session.run("black", *python_files) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests( + session: nox.sessions.Session, post_install: Callable = None +) -> None: + # 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.") + return + + 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.""" + concurrent_args = [] + 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") + with open("requirements.txt") as rfile: + packages = rfile.read() + + 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") + with open("requirements-test.txt") as rtfile: + packages += rtfile.read() + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + if "pytest-parallel" in packages: + concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto']) + elif "pytest-xdist" in packages: + concurrent_args.extend(['-n', 'auto']) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs + concurrent_args), + # 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) +def py(session: nox.sessions.Session) -> None: + """Runs py.test for a sample using the specified version of Python.""" + if session.python in TESTED_VERSIONS: + _session_tests(session) + else: + session.skip( + "SKIPPED: {} tests are disabled for this sample.".format(session.python) + ) + + +# +# Readmegen +# + + +def _get_repo_root() -> Optional[str]: + """ Returns the root folder of the project. """ + # Get root of this repository. Assume we don't have directories nested deeper than 10 items. + p = Path(os.getcwd()) + for i in range(10): + if p is None: + break + if Path(p / ".git").exists(): + return str(p) + # .git is not available in repos cloned via Cloud Build + # setup.py is always in the library's root, so use that instead + # https://github.com/googleapis/synthtool/issues/792 + if Path(p / "setup.py").exists(): + return str(p) + p = p.parent + raise Exception("Unable to detect repository root.") + + +GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")]) + + +@nox.session +@nox.parametrize("path", GENERATED_READMES) +def readmegen(session: nox.sessions.Session, path: str) -> None: + """(Re-)generates the readme for a sample.""" + session.install("jinja2", "pyyaml") + dir_ = os.path.dirname(path) + + if os.path.exists(os.path.join(dir_, "requirements.txt")): + session.install("-r", os.path.join(dir_, "requirements.txt")) + + in_file = os.path.join(dir_, "README.rst.in") + session.run( + "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file + ) diff --git a/samples/snippets/deletes/requirements-test.txt b/samples/snippets/deletes/requirements-test.txt new file mode 100644 index 000000000..d00689e06 --- /dev/null +++ b/samples/snippets/deletes/requirements-test.txt @@ -0,0 +1 @@ +pytest==7.1.2 diff --git a/samples/snippets/deletes/requirements.txt b/samples/snippets/deletes/requirements.txt new file mode 100644 index 000000000..d74172ad3 --- /dev/null +++ b/samples/snippets/deletes/requirements.txt @@ -0,0 +1,2 @@ +google-cloud-bigtable==2.9.0 +snapshottest==0.6.0 \ No newline at end of file diff --git a/samples/snippets/deletes/snapshots/__init__.py b/samples/snippets/deletes/snapshots/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/samples/snippets/deletes/snapshots/snap_deletes_test.py b/samples/snippets/deletes/snapshots/snap_deletes_test.py new file mode 100644 index 000000000..04a7db940 --- /dev/null +++ b/samples/snippets/deletes/snapshots/snap_deletes_test.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_check_and_mutate 1'] = '' + +snapshots['test_delete_column_family 1'] = '' + +snapshots['test_delete_from_column 1'] = '' + +snapshots['test_delete_from_column_family 1'] = '' + +snapshots['test_delete_from_row 1'] = '' + +snapshots['test_delete_table 1'] = '' + +snapshots['test_drop_row_range 1'] = '' + +snapshots['test_streaming_and_batching 1'] = '' From 10d00f5af5d5878c26529f5e48a5fb8d8385696d Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sun, 10 Jul 2022 13:05:58 -0400 Subject: [PATCH 04/11] fix: require python 3.7+ (#610) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(python): drop python 3.6 Source-Link: https://github.com/googleapis/synthtool/commit/4f89b13af10d086458f9b379e56a614f9d6dab7b Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:e7bb19d47c13839fe8c147e50e02e8b6cf5da8edd1af8b82208cd6f66cc2829c * require python 3.7+ in setup.py * remove python 3.6 sample configs * remove python 3.6 from noxfile.py * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * exclude templated README Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 4 +- .github/workflows/unittest.yml | 2 +- .kokoro/samples/python3.6/common.cfg | 40 --------- .kokoro/samples/python3.6/continuous.cfg | 7 -- .kokoro/samples/python3.6/periodic-head.cfg | 11 --- .kokoro/samples/python3.6/periodic.cfg | 6 -- .kokoro/samples/python3.6/presubmit.cfg | 6 -- .kokoro/test-samples-impl.sh | 4 +- CONTRIBUTING.rst | 6 +- README.rst | 4 +- noxfile.py | 85 ++++++++++++------- owlbot.py | 3 +- samples/beam/noxfile.py | 2 +- samples/hello/noxfile.py | 2 +- samples/hello_happybase/noxfile.py | 2 +- samples/instanceadmin/noxfile.py | 2 +- samples/metricscaler/noxfile.py | 2 +- samples/quickstart/noxfile.py | 2 +- samples/quickstart_happybase/noxfile.py | 2 +- samples/snippets/deletes/noxfile.py | 2 +- samples/snippets/filters/noxfile.py | 2 +- samples/snippets/reads/noxfile.py | 2 +- samples/snippets/writes/noxfile.py | 2 +- samples/tableadmin/noxfile.py | 2 +- .../templates/install_deps.tmpl.rst | 2 +- setup.py | 3 +- 26 files changed, 79 insertions(+), 128 deletions(-) delete mode 100644 .kokoro/samples/python3.6/common.cfg delete mode 100644 .kokoro/samples/python3.6/continuous.cfg delete mode 100644 .kokoro/samples/python3.6/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.6/periodic.cfg delete mode 100644 .kokoro/samples/python3.6/presubmit.cfg diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 50b29ffd2..1ce608523 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:b2dc5f80edcf5d4486c39068c9fa11f7f851d9568eea4dcba130f994ea9b5e97 -# created: 2022-06-12T16:09:31.61859086Z + digest: sha256:e7bb19d47c13839fe8c147e50e02e8b6cf5da8edd1af8b82208cd6f66cc2829c +# created: 2022-07-05T18:31:20.838186805Z diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index e5be6edbd..5531b0141 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.6', '3.7', '3.8', '3.9', '3.10'] + python: ['3.7', '3.8', '3.9', '3.10'] steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg deleted file mode 100644 index 21e188507..000000000 --- a/.kokoro/samples/python3.6/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.6" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-py36" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-bigtable/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-bigtable/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.6/continuous.cfg b/.kokoro/samples/python3.6/continuous.cfg deleted file mode 100644 index 7218af149..000000000 --- a/.kokoro/samples/python3.6/continuous.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - diff --git a/.kokoro/samples/python3.6/periodic-head.cfg b/.kokoro/samples/python3.6/periodic-head.cfg deleted file mode 100644 index be25a34f9..000000000 --- a/.kokoro/samples/python3.6/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-bigtable/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.6/periodic.cfg b/.kokoro/samples/python3.6/periodic.cfg deleted file mode 100644 index 71cd1e597..000000000 --- a/.kokoro/samples/python3.6/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.6/presubmit.cfg b/.kokoro/samples/python3.6/presubmit.cfg deleted file mode 100644 index a1c8d9759..000000000 --- a/.kokoro/samples/python3.6/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh index 8a324c9c7..2c6500cae 100755 --- a/.kokoro/test-samples-impl.sh +++ b/.kokoro/test-samples-impl.sh @@ -33,7 +33,7 @@ export PYTHONUNBUFFERED=1 env | grep KOKORO # Install nox -python3.6 -m pip install --upgrade --quiet nox +python3.9 -m pip install --upgrade --quiet nox # Use secrets acessor service account to get secrets if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then @@ -76,7 +76,7 @@ for file in samples/**/requirements.txt; do echo "------------------------------------------------------------" # Use nox to execute the tests for the project. - python3.6 -m nox -s "$RUN_TESTS_SESSION" + python3.9 -m nox -s "$RUN_TESTS_SESSION" EXIT=$? # If this is a periodic build, send the test log to the FlakyBot. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index a15cf6527..1579f6f6b 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -22,7 +22,7 @@ In order to add a feature: documentation. - The feature must work fully on the following CPython versions: - 3.6, 3.7, 3.8, 3.9 and 3.10 on both UNIX and Windows. + 3.7, 3.8, 3.9 and 3.10 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -221,13 +221,11 @@ Supported Python Versions We support: -- `Python 3.6`_ - `Python 3.7`_ - `Python 3.8`_ - `Python 3.9`_ - `Python 3.10`_ -.. _Python 3.6: https://docs.python.org/3.6/ .. _Python 3.7: https://docs.python.org/3.7/ .. _Python 3.8: https://docs.python.org/3.8/ .. _Python 3.9: https://docs.python.org/3.9/ @@ -239,7 +237,7 @@ Supported versions can be found in our ``noxfile.py`` `config`_. .. _config: https://github.com/googleapis/python-bigtable/blob/main/noxfile.py -We also explicitly decided to support Python 3 beginning with version 3.6. +We also explicitly decided to support Python 3 beginning with version 3.7. Reasons for this include: - Encouraging use of newest versions of Python 3 diff --git a/README.rst b/README.rst index 28cc372da..5f7d5809d 100644 --- a/README.rst +++ b/README.rst @@ -52,7 +52,7 @@ dependencies. Supported Python Versions ^^^^^^^^^^^^^^^^^^^^^^^^^ -Python >= 3.6 +Python >= 3.7 Deprecated Python Versions ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,6 +63,8 @@ Deprecated Python Versions - Python 3.5: the last released version which supported Python 3.5 was version 1.7.0, released 2021-02-09. +- Python 3.6: the last released version which supported Python 3.6 was + version v2.10.1, released 2022-06-03. Mac/Linux ^^^^^^^^^ diff --git a/noxfile.py b/noxfile.py index 2b89e8705..6875c9b44 100644 --- a/noxfile.py +++ b/noxfile.py @@ -31,7 +31,7 @@ DEFAULT_PYTHON_VERSION = "3.8" -UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +UNIT_TEST_PYTHON_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] UNIT_TEST_STANDARD_DEPENDENCIES = [ "mock", "asyncmock", @@ -370,28 +370,15 @@ def docfx(session): def prerelease_deps(session): """Run all tests with prerelease versions of dependencies installed.""" - prerel_deps = [ - "protobuf", - "googleapis-common-protos", - "google-auth", - "grpcio", - "grpcio-status", - "google-api-core", - "proto-plus", - # dependencies of google-auth - "cryptography", - "pyasn1", - ] - - for dep in prerel_deps: - session.install("--pre", "--no-deps", "--upgrade", dep) - - # Remaining dependencies - other_deps = ["requests"] - session.install(*other_deps) - + # Install all dependencies + session.install("-e", ".[all, tests, tracing]") session.install(*UNIT_TEST_STANDARD_DEPENDENCIES) - session.install(*SYSTEM_TEST_STANDARD_DEPENDENCIES) + system_deps_all = ( + SYSTEM_TEST_STANDARD_DEPENDENCIES + + SYSTEM_TEST_EXTERNAL_DEPENDENCIES + + SYSTEM_TEST_EXTRAS + ) + session.install(*system_deps_all) # Because we test minimum dependency versions on the minimum Python # version, the first version we test with in the unit tests sessions has a @@ -405,19 +392,44 @@ def prerelease_deps(session): constraints_text = constraints_file.read() # Ignore leading whitespace and comment lines. - deps = [ + constraints_deps = [ match.group(1) for match in re.finditer( r"^\s*(\S+)(?===\S+)", constraints_text, flags=re.MULTILINE ) ] - # Don't overwrite prerelease packages. - deps = [dep for dep in deps if dep not in prerel_deps] - # We use --no-deps to ensure that pre-release versions aren't overwritten - # by the version ranges in setup.py. - session.install(*deps) - session.install("--no-deps", "-e", ".[all]") + session.install(*constraints_deps) + + if os.path.exists("samples/snippets/requirements.txt"): + session.install("-r", "samples/snippets/requirements.txt") + + if os.path.exists("samples/snippets/requirements-test.txt"): + session.install("-r", "samples/snippets/requirements-test.txt") + + prerel_deps = [ + "protobuf", + # dependency of grpc + "six", + "googleapis-common-protos", + "grpcio", + "grpcio-status", + "google-api-core", + "proto-plus", + "google-cloud-testutils", + # dependencies of google-cloud-testutils" + "click", + ] + + for dep in prerel_deps: + session.install("--pre", "--no-deps", "--upgrade", dep) + + # Remaining dependencies + other_deps = [ + "requests", + "google-auth", + ] + session.install(*other_deps) # Print out prerelease package versions session.run( @@ -426,5 +438,16 @@ def prerelease_deps(session): session.run("python", "-c", "import grpc; print(grpc.__version__)") session.run("py.test", "tests/unit") - session.run("py.test", "tests/system") - session.run("py.test", "samples/snippets") + + system_test_path = os.path.join("tests", "system.py") + system_test_folder_path = os.path.join("tests", "system") + + # Only run system tests if found. + if os.path.exists(system_test_path) or os.path.exists(system_test_folder_path): + session.run("py.test", "tests/system") + + snippets_test_path = os.path.join("samples", "snippets") + + # Only run samples tests if found. + if os.path.exists(snippets_test_path): + session.run("py.test", "samples/snippets") diff --git a/owlbot.py b/owlbot.py index ba43cae94..7b35e1a9c 100644 --- a/owlbot.py +++ b/owlbot.py @@ -86,13 +86,12 @@ def get_staging_dirs( # ---------------------------------------------------------------------------- templated_files = common.py_library( samples=True, # set to True only if there are samples - unit_test_python_versions=["3.6", "3.7", "3.8", "3.9", "3.10"], split_system_tests=True, microgenerator=True, cov_level=100, ) -s.move(templated_files, excludes=[".coveragerc"]) +s.move(templated_files, excludes=[".coveragerc", "README.rst"]) # ---------------------------------------------------------------------------- # Customize noxfile.py diff --git a/samples/beam/noxfile.py b/samples/beam/noxfile.py index 960a011c2..908dd1a49 100644 --- a/samples/beam/noxfile.py +++ b/samples/beam/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/hello/noxfile.py b/samples/hello/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/hello/noxfile.py +++ b/samples/hello/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/hello_happybase/noxfile.py b/samples/hello_happybase/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/hello_happybase/noxfile.py +++ b/samples/hello_happybase/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/instanceadmin/noxfile.py b/samples/instanceadmin/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/instanceadmin/noxfile.py +++ b/samples/instanceadmin/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/metricscaler/noxfile.py b/samples/metricscaler/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/metricscaler/noxfile.py +++ b/samples/metricscaler/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/quickstart/noxfile.py b/samples/quickstart/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/quickstart/noxfile.py +++ b/samples/quickstart/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/quickstart_happybase/noxfile.py b/samples/quickstart_happybase/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/quickstart_happybase/noxfile.py +++ b/samples/quickstart_happybase/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/snippets/deletes/noxfile.py b/samples/snippets/deletes/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/snippets/deletes/noxfile.py +++ b/samples/snippets/deletes/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/snippets/filters/noxfile.py b/samples/snippets/filters/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/snippets/filters/noxfile.py +++ b/samples/snippets/filters/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/snippets/reads/noxfile.py b/samples/snippets/reads/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/snippets/reads/noxfile.py +++ b/samples/snippets/reads/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/snippets/writes/noxfile.py b/samples/snippets/writes/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/snippets/writes/noxfile.py +++ b/samples/snippets/writes/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/tableadmin/noxfile.py b/samples/tableadmin/noxfile.py index 38bb0a572..5fcb9d746 100644 --- a/samples/tableadmin/noxfile.py +++ b/samples/tableadmin/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst index 275d64989..6f069c6c8 100644 --- a/scripts/readme-gen/templates/install_deps.tmpl.rst +++ b/scripts/readme-gen/templates/install_deps.tmpl.rst @@ -12,7 +12,7 @@ Install Dependencies .. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup -#. Create a virtualenv. Samples are compatible with Python 3.6+. +#. Create a virtualenv. Samples are compatible with Python 3.7+. .. code-block:: bash diff --git a/setup.py b/setup.py index f2ea88af5..dce4f5910 100644 --- a/setup.py +++ b/setup.py @@ -80,7 +80,6 @@ "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", @@ -97,7 +96,7 @@ "scripts/fixup_bigtable_v2_keywords.py", "scripts/fixup_bigtable_admin_v2_keywords.py", ], - python_requires=">=3.6", + python_requires=">=3.7", include_package_data=True, zip_safe=False, ) From 2363e4dfb6276c50d6442fecfe56769366aa9eec Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Sun, 24 Jul 2022 13:29:18 +0200 Subject: [PATCH 05/11] chore(deps): update all dependencies (#601) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * revert * attempt to fix flaky test * use build specific project for test * attempt to fix flaky test Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/workflows/mypy.yml | 2 +- .github/workflows/system_emulated.yml | 2 +- samples/instanceadmin/requirements.txt | 2 +- samples/metricscaler/metricscaler.py | 14 ++++--- samples/metricscaler/metricscaler_test.py | 8 +++- samples/metricscaler/noxfile_config.py | 39 +++++++++++++++++++ samples/snippets/writes/requirements-test.txt | 2 +- samples/tableadmin/requirements-test.txt | 2 +- 8 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 samples/metricscaler/noxfile_config.py diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index f9f07f4de..c63242630 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -10,7 +10,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: "3.8" - name: Install nox diff --git a/.github/workflows/system_emulated.yml b/.github/workflows/system_emulated.yml index 303d36724..48b8faa42 100644 --- a/.github/workflows/system_emulated.yml +++ b/.github/workflows/system_emulated.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v3 - name: Setup Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: '3.8' diff --git a/samples/instanceadmin/requirements.txt b/samples/instanceadmin/requirements.txt index b11e1d430..7df9cb242 100644 --- a/samples/instanceadmin/requirements.txt +++ b/samples/instanceadmin/requirements.txt @@ -1,2 +1,2 @@ google-cloud-bigtable==2.10.0 -backoff==2.0.1 +backoff==2.1.2 diff --git a/samples/metricscaler/metricscaler.py b/samples/metricscaler/metricscaler.py index d29e40a39..3ffa95a00 100644 --- a/samples/metricscaler/metricscaler.py +++ b/samples/metricscaler/metricscaler.py @@ -122,20 +122,22 @@ def scale_bigtable(bigtable_instance, bigtable_cluster, scale_up): if current_node_count < max_node_count: new_node_count = min(current_node_count + size_change_step, max_node_count) cluster.serve_nodes = new_node_count - cluster.update() + operation = cluster.update() + response = operation.result(60) logger.info( - "Scaled up from {} to {} nodes.".format( - current_node_count, new_node_count + "Scaled up from {} to {} nodes for {}.".format( + current_node_count, new_node_count, response.name ) ) else: if current_node_count > min_node_count: new_node_count = max(current_node_count - size_change_step, min_node_count) cluster.serve_nodes = new_node_count - cluster.update() + operation = cluster.update() + response = operation.result(60) logger.info( - "Scaled down from {} to {} nodes.".format( - current_node_count, new_node_count + "Scaled down from {} to {} nodes for {}.".format( + current_node_count, new_node_count, response.name ) ) # [END bigtable_scale] diff --git a/samples/metricscaler/metricscaler_test.py b/samples/metricscaler/metricscaler_test.py index 4420605ec..52a2498cc 100644 --- a/samples/metricscaler/metricscaler_test.py +++ b/samples/metricscaler/metricscaler_test.py @@ -77,7 +77,9 @@ def instance(): serve_nodes=serve_nodes, default_storage_type=storage_type, ) - instance.create(clusters=[cluster]) + operation = instance.create(clusters=[cluster]) + response = operation.result(60) + print(f"Successfully created {response.name}") # Eventual consistency check retry_found = RetryResult(bool) @@ -105,7 +107,9 @@ def dev_instance(): cluster = instance.cluster( cluster_id, location_id=BIGTABLE_ZONE, default_storage_type=storage_type ) - instance.create(clusters=[cluster]) + operation = instance.create(clusters=[cluster]) + response = operation.result(60) + print(f"Successfully created {response.name}") # Eventual consistency check retry_found = RetryResult(bool) diff --git a/samples/metricscaler/noxfile_config.py b/samples/metricscaler/noxfile_config.py new file mode 100644 index 000000000..8a2d55bea --- /dev/null +++ b/samples/metricscaler/noxfile_config.py @@ -0,0 +1,39 @@ +# Copyright 2021 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. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be imported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": False, + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + "gcloud_project_env": "BUILD_SPECIFIC_GCLOUD_PROJECT", + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} diff --git a/samples/snippets/writes/requirements-test.txt b/samples/snippets/writes/requirements-test.txt index 4d92cc9aa..ce161d15f 100644 --- a/samples/snippets/writes/requirements-test.txt +++ b/samples/snippets/writes/requirements-test.txt @@ -1,2 +1,2 @@ -backoff==2.0.1 +backoff==2.1.2 pytest==7.1.2 diff --git a/samples/tableadmin/requirements-test.txt b/samples/tableadmin/requirements-test.txt index 69a7581b6..c497ac626 100644 --- a/samples/tableadmin/requirements-test.txt +++ b/samples/tableadmin/requirements-test.txt @@ -1,2 +1,2 @@ pytest==7.1.2 -google-cloud-testutils==1.3.1 +google-cloud-testutils==1.3.2 From a7a76998fad3c12215527e4ebb517a1526cc152e Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sun, 24 Jul 2022 08:02:23 -0400 Subject: [PATCH 06/11] fix(deps): require google-api-core>=1.32.0,>=2.8.0 (#608) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: add audience parameter feat: Add storage_utilization_gib_per_node to Autoscaling target feat: Cloud Bigtable Undelete Table service and message proto files * feat: add audience parameter PiperOrigin-RevId: 456827138 Source-Link: https://github.com/googleapis/googleapis/commit/23f1a157189581734c7a77cddfeb7c5bc1e440ae Source-Link: https://github.com/googleapis/googleapis-gen/commit/4075a8514f676691ec156688a5bbf183aa9893ce Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNDA3NWE4NTE0ZjY3NjY5MWVjMTU2Njg4YTViYmYxODNhYTk4OTNjZSJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * feat: Add storage_utilization_gib_per_node to Autoscaling target PiperOrigin-RevId: 457776307 Source-Link: https://github.com/googleapis/googleapis/commit/982bb695af9ea65d7c59764512585d8a0fc1f981 Source-Link: https://github.com/googleapis/googleapis-gen/commit/0aff3ebcc8fb3c10b671ee386b4d263012d3d013 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMGFmZjNlYmNjOGZiM2MxMGI2NzFlZTM4NmI0ZDI2MzAxMmQzZDAxMyJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * feat: Cloud Bigtable Undelete Table service and message proto files PiperOrigin-RevId: 457778403 Source-Link: https://github.com/googleapis/googleapis/commit/2b0fe3befa5ed45db7eb2d95c71e8fe61c98190d Source-Link: https://github.com/googleapis/googleapis-gen/commit/ca2a2c55cafca4b876305b0b12f76ce0f166ddcc Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiY2EyYTJjNTVjYWZjYTRiODc2MzA1YjBiMTJmNzZjZTBmMTY2ZGRjYyJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * chore: use gapic-generator-python 1.1.1 PiperOrigin-RevId: 459095142 Source-Link: https://github.com/googleapis/googleapis/commit/4f1be992601ed740a581a32cedc4e7b6c6a27793 Source-Link: https://github.com/googleapis/googleapis-gen/commit/ae686d9cde4fc3e36d0ac02efb8643b15890c1ed Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiYWU2ODZkOWNkZTRmYzNlMzZkMGFjMDJlZmI4NjQzYjE1ODkwYzFlZCJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Synchronize new proto/yaml changes. PiperOrigin-RevId: 459539123 Source-Link: https://github.com/googleapis/googleapis/commit/2e0497d3c875dec96549036bc2e3775e646c8cd5 Source-Link: https://github.com/googleapis/googleapis-gen/commit/d56d71bed8f8d7bc9771056f8c5f2671e7f114a3 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZDU2ZDcxYmVkOGY4ZDdiYzk3NzEwNTZmOGM1ZjI2NzFlN2YxMTRhMyJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix(deps): require google-api-core>=1.32.0,>=2.8.0 * trigger ci Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- google/cloud/bigtable_admin_v2/__init__.py | 4 + .../bigtable_admin_v2/gapic_metadata.json | 10 + .../bigtable_instance_admin/client.py | 1 + .../transports/base.py | 16 +- .../transports/grpc.py | 2 + .../transports/grpc_asyncio.py | 2 + .../bigtable_table_admin/async_client.py | 89 ++++++ .../services/bigtable_table_admin/client.py | 90 ++++++ .../bigtable_table_admin/transports/base.py | 30 +- .../bigtable_table_admin/transports/grpc.py | 31 ++ .../transports/grpc_asyncio.py | 31 ++ .../cloud/bigtable_admin_v2/types/__init__.py | 4 + .../types/bigtable_table_admin.py | 49 +++ .../cloud/bigtable_admin_v2/types/instance.py | 12 + google/cloud/bigtable_v2/__init__.py | 2 + .../bigtable_v2/services/bigtable/client.py | 1 + .../services/bigtable/transports/base.py | 16 +- .../services/bigtable/transports/grpc.py | 2 + .../bigtable/transports/grpc_asyncio.py | 2 + google/cloud/bigtable_v2/types/__init__.py | 4 + .../bigtable_v2/types/response_params.py | 57 ++++ scripts/fixup_bigtable_admin_v2_keywords.py | 1 + setup.py | 8 +- testing/constraints-3.6.txt | 13 - testing/constraints-3.7.txt | 3 +- .../test_bigtable_instance_admin.py | 52 ++++ .../test_bigtable_table_admin.py | 280 ++++++++++++++++++ tests/unit/gapic/bigtable_v2/test_bigtable.py | 52 ++++ 28 files changed, 828 insertions(+), 36 deletions(-) create mode 100644 google/cloud/bigtable_v2/types/response_params.py delete mode 100644 testing/constraints-3.6.txt diff --git a/google/cloud/bigtable_admin_v2/__init__.py b/google/cloud/bigtable_admin_v2/__init__.py index 3713dc1e8..d77671d8d 100644 --- a/google/cloud/bigtable_admin_v2/__init__.py +++ b/google/cloud/bigtable_admin_v2/__init__.py @@ -73,6 +73,8 @@ from .types.bigtable_table_admin import RestoreTableRequest from .types.bigtable_table_admin import SnapshotTableMetadata from .types.bigtable_table_admin import SnapshotTableRequest +from .types.bigtable_table_admin import UndeleteTableMetadata +from .types.bigtable_table_admin import UndeleteTableRequest from .types.bigtable_table_admin import UpdateBackupRequest from .types.common import OperationProgress from .types.common import StorageType @@ -164,6 +166,8 @@ "SnapshotTableRequest", "StorageType", "Table", + "UndeleteTableMetadata", + "UndeleteTableRequest", "UpdateAppProfileMetadata", "UpdateAppProfileRequest", "UpdateBackupRequest", diff --git a/google/cloud/bigtable_admin_v2/gapic_metadata.json b/google/cloud/bigtable_admin_v2/gapic_metadata.json index a843c42e0..a9294167a 100644 --- a/google/cloud/bigtable_admin_v2/gapic_metadata.json +++ b/google/cloud/bigtable_admin_v2/gapic_metadata.json @@ -339,6 +339,11 @@ "test_iam_permissions" ] }, + "UndeleteTable": { + "methods": [ + "undelete_table" + ] + }, "UpdateBackup": { "methods": [ "update_backup" @@ -454,6 +459,11 @@ "test_iam_permissions" ] }, + "UndeleteTable": { + "methods": [ + "undelete_table" + ] + }, "UpdateBackup": { "methods": [ "update_backup" diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py b/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py index fe14b82be..fc602bf42 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py @@ -548,6 +548,7 @@ def __init__( quota_project_id=client_options.quota_project_id, client_info=client_info, always_use_jwt_access=True, + api_audience=client_options.api_audience, ) def create_instance( diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py b/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py index 32261ac7b..a5e1c40d4 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py @@ -68,6 +68,7 @@ def __init__( quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, **kwargs, ) -> None: """Instantiate the transport. @@ -95,11 +96,6 @@ def __init__( be used for service account credentials. """ - # Save the hostname. Default to port 443 (HTTPS) if none is specified. - if ":" not in host: - host += ":443" - self._host = host - scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES} # Save the scopes. @@ -120,6 +116,11 @@ def __init__( credentials, _ = google.auth.default( **scopes_kwargs, quota_project_id=quota_project_id ) + # Don't apply audience if the credentials file passed from user. + if hasattr(credentials, "with_gdch_audience"): + credentials = credentials.with_gdch_audience( + api_audience if api_audience else host + ) # If the credentials are service account credentials, then always try to use self signed JWT. if ( @@ -132,6 +133,11 @@ def __init__( # Save the credentials. self._credentials = credentials + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py b/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py index db7d0c1c4..fcf0cd94f 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py @@ -67,6 +67,7 @@ def __init__( quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, ) -> None: """Instantiate the transport. @@ -163,6 +164,7 @@ def __init__( quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, ) if not self._grpc_channel: diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py b/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py index bf1b8a38e..efcb3ed80 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py @@ -112,6 +112,7 @@ def __init__( quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, ) -> None: """Instantiate the transport. @@ -208,6 +209,7 @@ def __init__( quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, ) if not self._grpc_channel: diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py index 0752459ee..ff852e12f 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py @@ -715,6 +715,95 @@ async def delete_table( metadata=metadata, ) + async def undelete_table( + self, + request: Union[bigtable_table_admin.UndeleteTableRequest, dict] = None, + *, + name: str = None, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Restores a specified table which was accidentally + deleted. + + Args: + request (Union[google.cloud.bigtable_admin_v2.types.UndeleteTableRequest, dict]): + The request object. Request message for + [google.bigtable.admin.v2.BigtableTableAdmin.UndeleteTable][google.bigtable.admin.v2.BigtableTableAdmin.UndeleteTable] + name (:class:`str`): + Required. The unique name of the table to be restored. + Values are of the form + ``projects/{project}/instances/{instance}/tables/{table}``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.bigtable_admin_v2.types.Table` A collection of user data indexed by row, column, and timestamp. + Each table is served using the resources of its + parent cluster. + + """ + # Create or coerce a protobuf request object. + # Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = bigtable_table_admin.UndeleteTableRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.undelete_table, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + table.Table, + metadata_type=bigtable_table_admin.UndeleteTableMetadata, + ) + + # Done; return the response. + return response + async def modify_column_families( self, request: Union[bigtable_table_admin.ModifyColumnFamiliesRequest, dict] = None, diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py index 3b1185e53..70f10e1de 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py @@ -550,6 +550,7 @@ def __init__( quota_project_id=client_options.quota_project_id, client_info=client_info, always_use_jwt_access=True, + api_audience=client_options.api_audience, ) def create_table( @@ -1019,6 +1020,95 @@ def delete_table( metadata=metadata, ) + def undelete_table( + self, + request: Union[bigtable_table_admin.UndeleteTableRequest, dict] = None, + *, + name: str = None, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Restores a specified table which was accidentally + deleted. + + Args: + request (Union[google.cloud.bigtable_admin_v2.types.UndeleteTableRequest, dict]): + The request object. Request message for + [google.bigtable.admin.v2.BigtableTableAdmin.UndeleteTable][google.bigtable.admin.v2.BigtableTableAdmin.UndeleteTable] + name (str): + Required. The unique name of the table to be restored. + Values are of the form + ``projects/{project}/instances/{instance}/tables/{table}``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.bigtable_admin_v2.types.Table` A collection of user data indexed by row, column, and timestamp. + Each table is served using the resources of its + parent cluster. + + """ + # Create or coerce a protobuf request object. + # Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a bigtable_table_admin.UndeleteTableRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, bigtable_table_admin.UndeleteTableRequest): + request = bigtable_table_admin.UndeleteTableRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.undelete_table] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + table.Table, + metadata_type=bigtable_table_admin.UndeleteTableMetadata, + ) + + # Done; return the response. + return response + def modify_column_families( self, request: Union[bigtable_table_admin.ModifyColumnFamiliesRequest, dict] = None, diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py index 5370416a0..db8222d89 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py @@ -68,6 +68,7 @@ def __init__( quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, **kwargs, ) -> None: """Instantiate the transport. @@ -95,11 +96,6 @@ def __init__( be used for service account credentials. """ - # Save the hostname. Default to port 443 (HTTPS) if none is specified. - if ":" not in host: - host += ":443" - self._host = host - scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES} # Save the scopes. @@ -120,6 +116,11 @@ def __init__( credentials, _ = google.auth.default( **scopes_kwargs, quota_project_id=quota_project_id ) + # Don't apply audience if the credentials file passed from user. + if hasattr(credentials, "with_gdch_audience"): + credentials = credentials.with_gdch_audience( + api_audience if api_audience else host + ) # If the credentials are service account credentials, then always try to use self signed JWT. if ( @@ -132,6 +133,11 @@ def __init__( # Save the credentials. self._credentials = credentials + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { @@ -180,6 +186,11 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), + self.undelete_table: gapic_v1.method.wrap_method( + self.undelete_table, + default_timeout=None, + client_info=client_info, + ), self.modify_column_families: gapic_v1.method.wrap_method( self.modify_column_families, default_timeout=300.0, @@ -409,6 +420,15 @@ def delete_table( ]: raise NotImplementedError() + @property + def undelete_table( + self, + ) -> Callable[ + [bigtable_table_admin.UndeleteTableRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + @property def modify_column_families( self, diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py index f6c2c478d..4c8e85609 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py @@ -69,6 +69,7 @@ def __init__( quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, ) -> None: """Instantiate the transport. @@ -165,6 +166,7 @@ def __init__( quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, ) if not self._grpc_channel: @@ -399,6 +401,35 @@ def delete_table( ) return self._stubs["delete_table"] + @property + def undelete_table( + self, + ) -> Callable[ + [bigtable_table_admin.UndeleteTableRequest], operations_pb2.Operation + ]: + r"""Return a callable for the undelete table method over gRPC. + + Restores a specified table which was accidentally + deleted. + + Returns: + Callable[[~.UndeleteTableRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "undelete_table" not in self._stubs: + self._stubs["undelete_table"] = self.grpc_channel.unary_unary( + "/google.bigtable.admin.v2.BigtableTableAdmin/UndeleteTable", + request_serializer=bigtable_table_admin.UndeleteTableRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["undelete_table"] + @property def modify_column_families( self, diff --git a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py index 7d2077f23..349f810a8 100644 --- a/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py +++ b/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py @@ -114,6 +114,7 @@ def __init__( quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, ) -> None: """Instantiate the transport. @@ -210,6 +211,7 @@ def __init__( quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, ) if not self._grpc_channel: @@ -409,6 +411,35 @@ def delete_table( ) return self._stubs["delete_table"] + @property + def undelete_table( + self, + ) -> Callable[ + [bigtable_table_admin.UndeleteTableRequest], Awaitable[operations_pb2.Operation] + ]: + r"""Return a callable for the undelete table method over gRPC. + + Restores a specified table which was accidentally + deleted. + + Returns: + Callable[[~.UndeleteTableRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "undelete_table" not in self._stubs: + self._stubs["undelete_table"] = self.grpc_channel.unary_unary( + "/google.bigtable.admin.v2.BigtableTableAdmin/UndeleteTable", + request_serializer=bigtable_table_admin.UndeleteTableRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["undelete_table"] + @property def modify_column_families( self, diff --git a/google/cloud/bigtable_admin_v2/types/__init__.py b/google/cloud/bigtable_admin_v2/types/__init__.py index f35f0f4ab..31f4b712f 100644 --- a/google/cloud/bigtable_admin_v2/types/__init__.py +++ b/google/cloud/bigtable_admin_v2/types/__init__.py @@ -70,6 +70,8 @@ RestoreTableRequest, SnapshotTableMetadata, SnapshotTableRequest, + UndeleteTableMetadata, + UndeleteTableRequest, UpdateBackupRequest, ) from .common import ( @@ -151,6 +153,8 @@ "RestoreTableRequest", "SnapshotTableMetadata", "SnapshotTableRequest", + "UndeleteTableMetadata", + "UndeleteTableRequest", "UpdateBackupRequest", "OperationProgress", "StorageType", diff --git a/google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py b/google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py index 6a366a5e4..2078ce922 100644 --- a/google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py +++ b/google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py @@ -35,6 +35,8 @@ "ListTablesResponse", "GetTableRequest", "DeleteTableRequest", + "UndeleteTableRequest", + "UndeleteTableMetadata", "ModifyColumnFamiliesRequest", "GenerateConsistencyTokenRequest", "GenerateConsistencyTokenResponse", @@ -459,6 +461,53 @@ class DeleteTableRequest(proto.Message): ) +class UndeleteTableRequest(proto.Message): + r"""Request message for + [google.bigtable.admin.v2.BigtableTableAdmin.UndeleteTable][google.bigtable.admin.v2.BigtableTableAdmin.UndeleteTable] + + Attributes: + name (str): + Required. The unique name of the table to be restored. + Values are of the form + ``projects/{project}/instances/{instance}/tables/{table}``. + """ + + name = proto.Field( + proto.STRING, + number=1, + ) + + +class UndeleteTableMetadata(proto.Message): + r"""Metadata type for the operation returned by + [google.bigtable.admin.v2.BigtableTableAdmin.UndeleteTable][google.bigtable.admin.v2.BigtableTableAdmin.UndeleteTable]. + + Attributes: + name (str): + The name of the table being restored. + start_time (google.protobuf.timestamp_pb2.Timestamp): + The time at which this operation started. + end_time (google.protobuf.timestamp_pb2.Timestamp): + If set, the time at which this operation + finished or was cancelled. + """ + + name = proto.Field( + proto.STRING, + number=1, + ) + start_time = proto.Field( + proto.MESSAGE, + number=2, + message=timestamp_pb2.Timestamp, + ) + end_time = proto.Field( + proto.MESSAGE, + number=3, + message=timestamp_pb2.Timestamp, + ) + + class ModifyColumnFamiliesRequest(proto.Message): r"""Request message for [google.bigtable.admin.v2.BigtableTableAdmin.ModifyColumnFamilies][google.bigtable.admin.v2.BigtableTableAdmin.ModifyColumnFamilies] diff --git a/google/cloud/bigtable_admin_v2/types/instance.py b/google/cloud/bigtable_admin_v2/types/instance.py index 1b2e4d615..06a2cf39d 100644 --- a/google/cloud/bigtable_admin_v2/types/instance.py +++ b/google/cloud/bigtable_admin_v2/types/instance.py @@ -125,12 +125,24 @@ class AutoscalingTargets(proto.Message): achieve. This number is on a scale from 0 (no utilization) to 100 (total utilization), and is limited between 10 and 80, otherwise it will return INVALID_ARGUMENT error. + storage_utilization_gib_per_node (int): + The storage utilization that the Autoscaler should be trying + to achieve. This number is limited between 2560 (2.5TiB) and + 5120 (5TiB) for a SSD cluster and between 8192 (8TiB) and + 16384 (16TiB) for an HDD cluster; otherwise it will return + INVALID_ARGUMENT error. If this value is set to 0, it will + be treated as if it were set to the default value: 2560 for + SSD, 8192 for HDD. """ cpu_utilization_percent = proto.Field( proto.INT32, number=2, ) + storage_utilization_gib_per_node = proto.Field( + proto.INT32, + number=3, + ) class AutoscalingLimits(proto.Message): diff --git a/google/cloud/bigtable_v2/__init__.py b/google/cloud/bigtable_v2/__init__.py index d744bd53f..5f2893c50 100644 --- a/google/cloud/bigtable_v2/__init__.py +++ b/google/cloud/bigtable_v2/__init__.py @@ -43,6 +43,7 @@ from .types.data import RowSet from .types.data import TimestampRange from .types.data import ValueRange +from .types.response_params import ResponseParams __all__ = ( "BigtableAsyncClient", @@ -65,6 +66,7 @@ "ReadModifyWriteRule", "ReadRowsRequest", "ReadRowsResponse", + "ResponseParams", "Row", "RowFilter", "RowRange", diff --git a/google/cloud/bigtable_v2/services/bigtable/client.py b/google/cloud/bigtable_v2/services/bigtable/client.py index a0dfdff4e..30dd2934f 100644 --- a/google/cloud/bigtable_v2/services/bigtable/client.py +++ b/google/cloud/bigtable_v2/services/bigtable/client.py @@ -441,6 +441,7 @@ def __init__( quota_project_id=client_options.quota_project_id, client_info=client_info, always_use_jwt_access=True, + api_audience=client_options.api_audience, ) def read_rows( diff --git a/google/cloud/bigtable_v2/services/bigtable/transports/base.py b/google/cloud/bigtable_v2/services/bigtable/transports/base.py index 922068fd0..1a6ed7554 100644 --- a/google/cloud/bigtable_v2/services/bigtable/transports/base.py +++ b/google/cloud/bigtable_v2/services/bigtable/transports/base.py @@ -61,6 +61,7 @@ def __init__( quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, **kwargs, ) -> None: """Instantiate the transport. @@ -88,11 +89,6 @@ def __init__( be used for service account credentials. """ - # Save the hostname. Default to port 443 (HTTPS) if none is specified. - if ":" not in host: - host += ":443" - self._host = host - scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES} # Save the scopes. @@ -113,6 +109,11 @@ def __init__( credentials, _ = google.auth.default( **scopes_kwargs, quota_project_id=quota_project_id ) + # Don't apply audience if the credentials file passed from user. + if hasattr(credentials, "with_gdch_audience"): + credentials = credentials.with_gdch_audience( + api_audience if api_audience else host + ) # If the credentials are service account credentials, then always try to use self signed JWT. if ( @@ -125,6 +126,11 @@ def __init__( # Save the credentials. self._credentials = credentials + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { diff --git a/google/cloud/bigtable_v2/services/bigtable/transports/grpc.py b/google/cloud/bigtable_v2/services/bigtable/transports/grpc.py index ce40cf335..b453d3bc0 100644 --- a/google/cloud/bigtable_v2/services/bigtable/transports/grpc.py +++ b/google/cloud/bigtable_v2/services/bigtable/transports/grpc.py @@ -59,6 +59,7 @@ def __init__( quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, ) -> None: """Instantiate the transport. @@ -154,6 +155,7 @@ def __init__( quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, ) if not self._grpc_channel: diff --git a/google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py b/google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py index 4099e7bd7..88081f30a 100644 --- a/google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py +++ b/google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py @@ -104,6 +104,7 @@ def __init__( quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, + api_audience: Optional[str] = None, ) -> None: """Instantiate the transport. @@ -199,6 +200,7 @@ def __init__( quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, ) if not self._grpc_channel: diff --git a/google/cloud/bigtable_v2/types/__init__.py b/google/cloud/bigtable_v2/types/__init__.py index 401705715..ec6fbafd4 100644 --- a/google/cloud/bigtable_v2/types/__init__.py +++ b/google/cloud/bigtable_v2/types/__init__.py @@ -43,6 +43,9 @@ TimestampRange, ValueRange, ) +from .response_params import ( + ResponseParams, +) __all__ = ( "CheckAndMutateRowRequest", @@ -71,4 +74,5 @@ "RowSet", "TimestampRange", "ValueRange", + "ResponseParams", ) diff --git a/google/cloud/bigtable_v2/types/response_params.py b/google/cloud/bigtable_v2/types/response_params.py new file mode 100644 index 000000000..b11cffeef --- /dev/null +++ b/google/cloud/bigtable_v2/types/response_params.py @@ -0,0 +1,57 @@ +# -*- 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. +# +import proto # type: ignore + + +__protobuf__ = proto.module( + package="google.bigtable.v2", + manifest={ + "ResponseParams", + }, +) + + +class ResponseParams(proto.Message): + r"""Response metadata proto This is an experimental feature that will be + used to get zone_id and cluster_id from response trailers to tag the + metrics. This should not be used by customers directly + + Attributes: + zone_id (str): + The cloud bigtable zone associated with the + cluster. + + This field is a member of `oneof`_ ``_zone_id``. + cluster_id (str): + Identifier for a cluster that represents set + of bigtable resources. + + This field is a member of `oneof`_ ``_cluster_id``. + """ + + zone_id = proto.Field( + proto.STRING, + number=1, + optional=True, + ) + cluster_id = proto.Field( + proto.STRING, + number=2, + optional=True, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/scripts/fixup_bigtable_admin_v2_keywords.py b/scripts/fixup_bigtable_admin_v2_keywords.py index 72354ba16..9622469c4 100644 --- a/scripts/fixup_bigtable_admin_v2_keywords.py +++ b/scripts/fixup_bigtable_admin_v2_keywords.py @@ -75,6 +75,7 @@ class bigtable_adminCallTransformer(cst.CSTTransformer): 'set_iam_policy': ('resource', 'policy', 'update_mask', ), 'snapshot_table': ('name', 'cluster', 'snapshot_id', 'ttl', 'description', ), 'test_iam_permissions': ('resource', 'permissions', ), + 'undelete_table': ('name', ), 'update_app_profile': ('app_profile', 'update_mask', 'ignore_warnings', ), 'update_backup': ('backup', 'update_mask', ), 'update_cluster': ('name', 'location', 'state', 'serve_nodes', 'cluster_config', 'default_storage_type', 'encryption_config', ), diff --git a/setup.py b/setup.py index dce4f5910..110101e5e 100644 --- a/setup.py +++ b/setup.py @@ -29,13 +29,7 @@ # 'Development Status :: 5 - Production/Stable' release_status = "Development Status :: 5 - Production/Stable" dependencies = [ - # NOTE: Maintainers, please do not require google-api-core>=2.x.x - # Until this issue is closed - # https://github.com/googleapis/google-cloud-python/issues/10566 - "google-api-core[grpc] >= 1.31.5, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.0", - # NOTE: Maintainers, please do not require google-api-core>=2.x.x - # Until this issue is closed - # https://github.com/googleapis/google-cloud-python/issues/10566 + "google-api-core[grpc] >= 1.32.0, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*", "google-cloud-core >= 1.4.1, <3.0.0dev", "grpc-google-iam-v1 >= 0.12.4, <1.0.0dev", "proto-plus >= 1.18.0, <2.0.0dev", diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt deleted file mode 100644 index 455fa9b7b..000000000 --- a/testing/constraints-3.6.txt +++ /dev/null @@ -1,13 +0,0 @@ -# This constraints file is used to check that lower bounds -# are correct in setup.py -# List *all* library dependencies and extras in this file. -# Pin the version to the lower bound. -# -# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", -# Then this file should have foo==1.14.0 -google-api-core==1.31.5 -google-cloud-core==1.4.1 -grpc-google-iam-v1==0.12.4 -proto-plus==1.18.0 -libcst==0.2.5 -protobuf==3.19.0 diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index 455fa9b7b..4847b9e04 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -5,9 +5,10 @@ # # e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", # Then this file should have foo==1.14.0 -google-api-core==1.31.5 +google-api-core==1.32.0 google-cloud-core==1.4.1 grpc-google-iam-v1==0.12.4 proto-plus==1.18.0 libcst==0.2.5 protobuf==3.19.0 + diff --git a/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py b/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py index 4e7fcf2a7..7d61a067c 100644 --- a/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py +++ b/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py @@ -251,6 +251,7 @@ def test_bigtable_instance_admin_client_client_options( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is @@ -268,6 +269,7 @@ def test_bigtable_instance_admin_client_client_options( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is @@ -285,6 +287,7 @@ def test_bigtable_instance_admin_client_client_options( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has @@ -314,6 +317,25 @@ def test_bigtable_instance_admin_client_client_options( quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, + ) + # Check the case api_endpoint is provided + options = client_options.ClientOptions( + api_audience="https://language.googleapis.com" + ) + 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=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, + api_audience="https://language.googleapis.com", ) @@ -391,6 +413,7 @@ def test_bigtable_instance_admin_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case ADC client cert is provided. Whether client cert is used depends on @@ -425,6 +448,7 @@ def test_bigtable_instance_admin_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case client_cert_source and ADC client cert are not provided. @@ -447,6 +471,7 @@ def test_bigtable_instance_admin_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -565,6 +590,7 @@ def test_bigtable_instance_admin_client_client_options_scopes( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -603,6 +629,7 @@ def test_bigtable_instance_admin_client_client_options_credentials_file( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -623,6 +650,7 @@ def test_bigtable_instance_admin_client_client_options_from_dict(): quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -661,6 +689,7 @@ def test_bigtable_instance_admin_client_create_channel_credentials_file( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # test that the credentials from file are saved and used as the credentials. @@ -6330,6 +6359,28 @@ def test_bigtable_instance_admin_transport_auth_adc(transport_class): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.BigtableInstanceAdminGrpcTransport, + transports.BigtableInstanceAdminGrpcAsyncIOTransport, + ], +) +def test_bigtable_instance_admin_transport_auth_gdch_credentials(transport_class): + host = "https://language.com" + api_audience_tests = [None, "https://language2.com"] + api_audience_expect = [host, "https://language2.com"] + for t, e in zip(api_audience_tests, api_audience_expect): + with mock.patch.object(google.auth, "default", autospec=True) as adc: + gdch_mock = mock.MagicMock() + type(gdch_mock).with_gdch_audience = mock.PropertyMock( + return_value=gdch_mock + ) + adc.return_value = (gdch_mock, None) + transport_class(host=host, api_audience=t) + gdch_mock.with_gdch_audience.assert_called_once_with(e) + + @pytest.mark.parametrize( "transport_class,grpc_helpers", [ @@ -6988,4 +7039,5 @@ def test_api_key_credentials(client_class, transport_class): quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) diff --git a/tests/unit/gapic/bigtable_admin_v2/test_bigtable_table_admin.py b/tests/unit/gapic/bigtable_admin_v2/test_bigtable_table_admin.py index ff766dcd1..adcf50b1e 100644 --- a/tests/unit/gapic/bigtable_admin_v2/test_bigtable_table_admin.py +++ b/tests/unit/gapic/bigtable_admin_v2/test_bigtable_table_admin.py @@ -249,6 +249,7 @@ def test_bigtable_table_admin_client_client_options( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is @@ -266,6 +267,7 @@ def test_bigtable_table_admin_client_client_options( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is @@ -283,6 +285,7 @@ def test_bigtable_table_admin_client_client_options( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has @@ -312,6 +315,25 @@ def test_bigtable_table_admin_client_client_options( quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, + ) + # Check the case api_endpoint is provided + options = client_options.ClientOptions( + api_audience="https://language.googleapis.com" + ) + 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=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, + api_audience="https://language.googleapis.com", ) @@ -389,6 +411,7 @@ def test_bigtable_table_admin_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case ADC client cert is provided. Whether client cert is used depends on @@ -423,6 +446,7 @@ def test_bigtable_table_admin_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case client_cert_source and ADC client cert are not provided. @@ -445,6 +469,7 @@ def test_bigtable_table_admin_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -559,6 +584,7 @@ def test_bigtable_table_admin_client_client_options_scopes( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -597,6 +623,7 @@ def test_bigtable_table_admin_client_client_options_credentials_file( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -617,6 +644,7 @@ def test_bigtable_table_admin_client_client_options_from_dict(): quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -655,6 +683,7 @@ def test_bigtable_table_admin_client_create_channel_credentials_file( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # test that the credentials from file are saved and used as the credentials. @@ -2080,6 +2109,233 @@ async def test_delete_table_flattened_error_async(): ) +@pytest.mark.parametrize( + "request_type", + [ + bigtable_table_admin.UndeleteTableRequest, + dict, + ], +) +def test_undelete_table(request_type, transport: str = "grpc"): + client = BigtableTableAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_table), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.undelete_table(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == bigtable_table_admin.UndeleteTableRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_undelete_table_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = BigtableTableAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_table), "__call__") as call: + client.undelete_table() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == bigtable_table_admin.UndeleteTableRequest() + + +@pytest.mark.asyncio +async def test_undelete_table_async( + transport: str = "grpc_asyncio", + request_type=bigtable_table_admin.UndeleteTableRequest, +): + client = BigtableTableAdminAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_table), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.undelete_table(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == bigtable_table_admin.UndeleteTableRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_undelete_table_async_from_dict(): + await test_undelete_table_async(request_type=dict) + + +def test_undelete_table_field_headers(): + client = BigtableTableAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = bigtable_table_admin.UndeleteTableRequest() + + request.name = "name_value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_table), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.undelete_table(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=name_value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_undelete_table_field_headers_async(): + client = BigtableTableAdminAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = bigtable_table_admin.UndeleteTableRequest() + + request.name = "name_value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_table), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.undelete_table(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=name_value", + ) in kw["metadata"] + + +def test_undelete_table_flattened(): + client = BigtableTableAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_table), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.undelete_table( + name="name_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + arg = args[0].name + mock_val = "name_value" + assert arg == mock_val + + +def test_undelete_table_flattened_error(): + client = BigtableTableAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.undelete_table( + bigtable_table_admin.UndeleteTableRequest(), + name="name_value", + ) + + +@pytest.mark.asyncio +async def test_undelete_table_flattened_async(): + client = BigtableTableAdminAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_table), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.undelete_table( + name="name_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + arg = args[0].name + mock_val = "name_value" + assert arg == mock_val + + +@pytest.mark.asyncio +async def test_undelete_table_flattened_error_async(): + client = BigtableTableAdminAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.undelete_table( + bigtable_table_admin.UndeleteTableRequest(), + name="name_value", + ) + + @pytest.mark.parametrize( "request_type", [ @@ -6584,6 +6840,7 @@ def test_bigtable_table_admin_base_transport(): "list_tables", "get_table", "delete_table", + "undelete_table", "modify_column_families", "drop_row_range", "generate_consistency_token", @@ -6708,6 +6965,28 @@ def test_bigtable_table_admin_transport_auth_adc(transport_class): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.BigtableTableAdminGrpcTransport, + transports.BigtableTableAdminGrpcAsyncIOTransport, + ], +) +def test_bigtable_table_admin_transport_auth_gdch_credentials(transport_class): + host = "https://language.com" + api_audience_tests = [None, "https://language2.com"] + api_audience_expect = [host, "https://language2.com"] + for t, e in zip(api_audience_tests, api_audience_expect): + with mock.patch.object(google.auth, "default", autospec=True) as adc: + gdch_mock = mock.MagicMock() + type(gdch_mock).with_gdch_audience = mock.PropertyMock( + return_value=gdch_mock + ) + adc.return_value = (gdch_mock, None) + transport_class(host=host, api_audience=t) + gdch_mock.with_gdch_audience.assert_called_once_with(e) + + @pytest.mark.parametrize( "transport_class,grpc_helpers", [ @@ -7365,4 +7644,5 @@ def test_api_key_credentials(client_class, transport_class): quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) diff --git a/tests/unit/gapic/bigtable_v2/test_bigtable.py b/tests/unit/gapic/bigtable_v2/test_bigtable.py index 3ca5041c2..644265d2b 100644 --- a/tests/unit/gapic/bigtable_v2/test_bigtable.py +++ b/tests/unit/gapic/bigtable_v2/test_bigtable.py @@ -211,6 +211,7 @@ def test_bigtable_client_client_options(client_class, transport_class, transport quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is @@ -228,6 +229,7 @@ def test_bigtable_client_client_options(client_class, transport_class, transport quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is @@ -245,6 +247,7 @@ def test_bigtable_client_client_options(client_class, transport_class, transport quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has @@ -274,6 +277,25 @@ def test_bigtable_client_client_options(client_class, transport_class, transport quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, + ) + # Check the case api_endpoint is provided + options = client_options.ClientOptions( + api_audience="https://language.googleapis.com" + ) + 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=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, + api_audience="https://language.googleapis.com", ) @@ -339,6 +361,7 @@ def test_bigtable_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case ADC client cert is provided. Whether client cert is used depends on @@ -373,6 +396,7 @@ def test_bigtable_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # Check the case client_cert_source and ADC client cert are not provided. @@ -395,6 +419,7 @@ def test_bigtable_client_mtls_env_auto( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -501,6 +526,7 @@ def test_bigtable_client_client_options_scopes( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -534,6 +560,7 @@ def test_bigtable_client_client_options_credentials_file( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -552,6 +579,7 @@ def test_bigtable_client_client_options_from_dict(): quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) @@ -585,6 +613,7 @@ def test_bigtable_client_create_channel_credentials_file( quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) # test that the credentials from file are saved and used as the credentials. @@ -2650,6 +2679,28 @@ def test_bigtable_transport_auth_adc(transport_class): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.BigtableGrpcTransport, + transports.BigtableGrpcAsyncIOTransport, + ], +) +def test_bigtable_transport_auth_gdch_credentials(transport_class): + host = "https://language.com" + api_audience_tests = [None, "https://language2.com"] + api_audience_expect = [host, "https://language2.com"] + for t, e in zip(api_audience_tests, api_audience_expect): + with mock.patch.object(google.auth, "default", autospec=True) as adc: + gdch_mock = mock.MagicMock() + type(gdch_mock).with_gdch_audience = mock.PropertyMock( + return_value=gdch_mock + ) + adc.return_value = (gdch_mock, None) + transport_class(host=host, api_audience=t) + gdch_mock.with_gdch_audience.assert_called_once_with(e) + + @pytest.mark.parametrize( "transport_class,grpc_helpers", [ @@ -3137,4 +3188,5 @@ def test_api_key_credentials(client_class, transport_class): quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, always_use_jwt_access=True, + api_audience=None, ) From 8a0e7d8444e4881c8b5e9f9791c19f7f3e0c2625 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 22:24:08 -0400 Subject: [PATCH 07/11] chore(python): fix prerelease session [autoapprove] (#613) Source-Link: https://github.com/googleapis/synthtool/commit/1b9ad7694e44ddb4d9844df55ff7af77b51a4435 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:9db98b055a7f8bd82351238ccaacfd3cda58cdf73012ab58b8da146368330021 Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- noxfile.py | 33 ++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 1ce608523..0eb02fda4 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:e7bb19d47c13839fe8c147e50e02e8b6cf5da8edd1af8b82208cd6f66cc2829c -# created: 2022-07-05T18:31:20.838186805Z + digest: sha256:9db98b055a7f8bd82351238ccaacfd3cda58cdf73012ab58b8da146368330021 +# created: 2022-07-25T16:02:49.174178716Z diff --git a/noxfile.py b/noxfile.py index 6875c9b44..1c208b4c3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -372,7 +372,8 @@ def prerelease_deps(session): # Install all dependencies session.install("-e", ".[all, tests, tracing]") - session.install(*UNIT_TEST_STANDARD_DEPENDENCIES) + unit_deps_all = UNIT_TEST_STANDARD_DEPENDENCIES + UNIT_TEST_EXTERNAL_DEPENDENCIES + session.install(*unit_deps_all) system_deps_all = ( SYSTEM_TEST_STANDARD_DEPENDENCIES + SYSTEM_TEST_EXTERNAL_DEPENDENCIES @@ -401,12 +402,6 @@ def prerelease_deps(session): session.install(*constraints_deps) - if os.path.exists("samples/snippets/requirements.txt"): - session.install("-r", "samples/snippets/requirements.txt") - - if os.path.exists("samples/snippets/requirements-test.txt"): - session.install("-r", "samples/snippets/requirements-test.txt") - prerel_deps = [ "protobuf", # dependency of grpc @@ -443,11 +438,19 @@ def prerelease_deps(session): system_test_folder_path = os.path.join("tests", "system") # Only run system tests if found. - if os.path.exists(system_test_path) or os.path.exists(system_test_folder_path): - session.run("py.test", "tests/system") - - snippets_test_path = os.path.join("samples", "snippets") - - # Only run samples tests if found. - if os.path.exists(snippets_test_path): - session.run("py.test", "samples/snippets") + if os.path.exists(system_test_path): + session.run( + "py.test", + "--verbose", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_path, + *session.posargs, + ) + if os.path.exists(system_test_folder_path): + session.run( + "py.test", + "--verbose", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_folder_path, + *session.posargs, + ) From 7dc1469fef2dc38f1509b35a37e9c97381ab7601 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 11:33:52 -0400 Subject: [PATCH 08/11] feat: add satisfies_pzs output only field (#614) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Publish new fields PiperOrigin-RevId: 463378622 Source-Link: https://github.com/googleapis/googleapis/commit/8229ab4d3cee2d9c9da62805705599893971d5c2 Source-Link: https://github.com/googleapis/googleapis-gen/commit/bb82d0422b8f2f413158623a9d1e09422b2604b5 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiYmI4MmQwNDIyYjhmMmY0MTMxNTg2MjNhOWQxZTA5NDIyYjI2MDRiNSJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot --- google/cloud/bigtable_admin_v2/types/instance.py | 9 +++++++++ scripts/fixup_bigtable_admin_v2_keywords.py | 2 +- .../bigtable_admin_v2/test_bigtable_instance_admin.py | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/google/cloud/bigtable_admin_v2/types/instance.py b/google/cloud/bigtable_admin_v2/types/instance.py index 06a2cf39d..12422930e 100644 --- a/google/cloud/bigtable_admin_v2/types/instance.py +++ b/google/cloud/bigtable_admin_v2/types/instance.py @@ -71,6 +71,10 @@ class Instance(proto.Message): this Instance was created. For instances created before this field was added (August 2021), this value is ``seconds: 0, nanos: 1``. + satisfies_pzs (bool): + Output only. Reserved for future use. + + This field is a member of `oneof`_ ``_satisfies_pzs``. """ class State(proto.Enum): @@ -113,6 +117,11 @@ class Type(proto.Enum): number=7, message=timestamp_pb2.Timestamp, ) + satisfies_pzs = proto.Field( + proto.BOOL, + number=8, + optional=True, + ) class AutoscalingTargets(proto.Message): diff --git a/scripts/fixup_bigtable_admin_v2_keywords.py b/scripts/fixup_bigtable_admin_v2_keywords.py index 9622469c4..7623d5593 100644 --- a/scripts/fixup_bigtable_admin_v2_keywords.py +++ b/scripts/fixup_bigtable_admin_v2_keywords.py @@ -79,7 +79,7 @@ class bigtable_adminCallTransformer(cst.CSTTransformer): 'update_app_profile': ('app_profile', 'update_mask', 'ignore_warnings', ), 'update_backup': ('backup', 'update_mask', ), 'update_cluster': ('name', 'location', 'state', 'serve_nodes', 'cluster_config', 'default_storage_type', 'encryption_config', ), - 'update_instance': ('display_name', 'name', 'state', 'type_', 'labels', 'create_time', ), + 'update_instance': ('display_name', 'name', 'state', 'type_', 'labels', 'create_time', 'satisfies_pzs', ), } def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: diff --git a/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py b/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py index 7d61a067c..d4f52ecba 100644 --- a/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py +++ b/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py @@ -1011,6 +1011,7 @@ def test_get_instance(request_type, transport: str = "grpc"): display_name="display_name_value", state=instance.Instance.State.READY, type_=instance.Instance.Type.PRODUCTION, + satisfies_pzs=True, ) response = client.get_instance(request) @@ -1025,6 +1026,7 @@ def test_get_instance(request_type, transport: str = "grpc"): assert response.display_name == "display_name_value" assert response.state == instance.Instance.State.READY assert response.type_ == instance.Instance.Type.PRODUCTION + assert response.satisfies_pzs is True def test_get_instance_empty_call(): @@ -1066,6 +1068,7 @@ async def test_get_instance_async( display_name="display_name_value", state=instance.Instance.State.READY, type_=instance.Instance.Type.PRODUCTION, + satisfies_pzs=True, ) ) response = await client.get_instance(request) @@ -1081,6 +1084,7 @@ async def test_get_instance_async( assert response.display_name == "display_name_value" assert response.state == instance.Instance.State.READY assert response.type_ == instance.Instance.Type.PRODUCTION + assert response.satisfies_pzs is True @pytest.mark.asyncio @@ -1490,6 +1494,7 @@ def test_update_instance(request_type, transport: str = "grpc"): display_name="display_name_value", state=instance.Instance.State.READY, type_=instance.Instance.Type.PRODUCTION, + satisfies_pzs=True, ) response = client.update_instance(request) @@ -1504,6 +1509,7 @@ def test_update_instance(request_type, transport: str = "grpc"): assert response.display_name == "display_name_value" assert response.state == instance.Instance.State.READY assert response.type_ == instance.Instance.Type.PRODUCTION + assert response.satisfies_pzs is True def test_update_instance_empty_call(): @@ -1544,6 +1550,7 @@ async def test_update_instance_async( display_name="display_name_value", state=instance.Instance.State.READY, type_=instance.Instance.Type.PRODUCTION, + satisfies_pzs=True, ) ) response = await client.update_instance(request) @@ -1559,6 +1566,7 @@ async def test_update_instance_async( assert response.display_name == "display_name_value" assert response.state == instance.Instance.State.READY assert response.type_ == instance.Instance.Type.PRODUCTION + assert response.satisfies_pzs is True @pytest.mark.asyncio From 7bad13a65b1c249724f2928cec488d4040e3aa69 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 2 Aug 2022 14:57:02 +0200 Subject: [PATCH 09/11] chore(deps): update all dependencies (#616) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * revert * chore: update timeout to cater for duration of LRO Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- samples/beam/requirements.txt | 6 +++--- samples/hello/requirements.txt | 4 ++-- samples/instanceadmin/instanceadmin.py | 2 +- samples/instanceadmin/requirements.txt | 2 +- samples/metricscaler/requirements.txt | 4 ++-- samples/quickstart/requirements.txt | 2 +- samples/snippets/deletes/requirements.txt | 2 +- samples/snippets/filters/requirements.txt | 2 +- samples/snippets/reads/requirements.txt | 2 +- samples/snippets/writes/requirements.txt | 2 +- samples/tableadmin/requirements-test.txt | 2 +- samples/tableadmin/requirements.txt | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/samples/beam/requirements.txt b/samples/beam/requirements.txt index d3294d0f2..52da23f84 100644 --- a/samples/beam/requirements.txt +++ b/samples/beam/requirements.txt @@ -1,3 +1,3 @@ -apache-beam==2.39.0 -google-cloud-bigtable==2.10.0 -google-cloud-core==2.3.0 +apache-beam==2.40.0 +google-cloud-bigtable==2.10.1 +google-cloud-core==2.3.2 diff --git a/samples/hello/requirements.txt b/samples/hello/requirements.txt index 7d7271118..b6b1a2001 100644 --- a/samples/hello/requirements.txt +++ b/samples/hello/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-bigtable==2.10.0 -google-cloud-core==2.3.0 +google-cloud-bigtable==2.10.1 +google-cloud-core==2.3.2 diff --git a/samples/instanceadmin/instanceadmin.py b/samples/instanceadmin/instanceadmin.py index 13234f6c7..239b1dbae 100644 --- a/samples/instanceadmin/instanceadmin.py +++ b/samples/instanceadmin/instanceadmin.py @@ -159,7 +159,7 @@ def add_cluster(project_id, instance_id, cluster_id): else: operation = cluster.create() # Ensure the operation completes. - operation.result(timeout=60) + operation.result(timeout=120) print("\nCluster created: {}".format(cluster_id)) # [END bigtable_create_cluster] diff --git a/samples/instanceadmin/requirements.txt b/samples/instanceadmin/requirements.txt index 7df9cb242..e5d1d4ee6 100644 --- a/samples/instanceadmin/requirements.txt +++ b/samples/instanceadmin/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-bigtable==2.10.0 +google-cloud-bigtable==2.10.1 backoff==2.1.2 diff --git a/samples/metricscaler/requirements.txt b/samples/metricscaler/requirements.txt index 565d94417..0e95d91b7 100644 --- a/samples/metricscaler/requirements.txt +++ b/samples/metricscaler/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-bigtable==2.10.0 -google-cloud-monitoring==2.9.1 +google-cloud-bigtable==2.10.1 +google-cloud-monitoring==2.10.1 diff --git a/samples/quickstart/requirements.txt b/samples/quickstart/requirements.txt index 8d9f1299d..d0504d16d 100644 --- a/samples/quickstart/requirements.txt +++ b/samples/quickstart/requirements.txt @@ -1 +1 @@ -google-cloud-bigtable==2.10.0 +google-cloud-bigtable==2.10.1 diff --git a/samples/snippets/deletes/requirements.txt b/samples/snippets/deletes/requirements.txt index d74172ad3..9e6d5e6f3 100644 --- a/samples/snippets/deletes/requirements.txt +++ b/samples/snippets/deletes/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-bigtable==2.9.0 +google-cloud-bigtable==2.10.1 snapshottest==0.6.0 \ No newline at end of file diff --git a/samples/snippets/filters/requirements.txt b/samples/snippets/filters/requirements.txt index 0522dcf65..9e6d5e6f3 100644 --- a/samples/snippets/filters/requirements.txt +++ b/samples/snippets/filters/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-bigtable==2.10.0 +google-cloud-bigtable==2.10.1 snapshottest==0.6.0 \ No newline at end of file diff --git a/samples/snippets/reads/requirements.txt b/samples/snippets/reads/requirements.txt index 0522dcf65..9e6d5e6f3 100644 --- a/samples/snippets/reads/requirements.txt +++ b/samples/snippets/reads/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-bigtable==2.10.0 +google-cloud-bigtable==2.10.1 snapshottest==0.6.0 \ No newline at end of file diff --git a/samples/snippets/writes/requirements.txt b/samples/snippets/writes/requirements.txt index 53b03cadb..fb0107cad 100644 --- a/samples/snippets/writes/requirements.txt +++ b/samples/snippets/writes/requirements.txt @@ -1 +1 @@ -google-cloud-bigtable==2.10.0 \ No newline at end of file +google-cloud-bigtable==2.10.1 \ No newline at end of file diff --git a/samples/tableadmin/requirements-test.txt b/samples/tableadmin/requirements-test.txt index c497ac626..7f627052e 100644 --- a/samples/tableadmin/requirements-test.txt +++ b/samples/tableadmin/requirements-test.txt @@ -1,2 +1,2 @@ pytest==7.1.2 -google-cloud-testutils==1.3.2 +google-cloud-testutils==1.3.3 diff --git a/samples/tableadmin/requirements.txt b/samples/tableadmin/requirements.txt index 8d9f1299d..d0504d16d 100644 --- a/samples/tableadmin/requirements.txt +++ b/samples/tableadmin/requirements.txt @@ -1 +1 @@ -google-cloud-bigtable==2.10.0 +google-cloud-bigtable==2.10.1 From b4853e59d0efd8a7b37f3fcb06b14dbd9f5d20a4 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Thu, 4 Aug 2022 17:16:12 -0400 Subject: [PATCH 10/11] perf: improve row merging (#619) The underlying GAPIC client uses protoplus for all requests and responses. However the underlying protos for ReadRowsResponse are never exposed to end users directly: the underlying chunks get merged into logic rows. The readability benefits provided by protoplus for ReadRows do not justify the costs. This change unwraps the protoplus messages and uses the raw protobuff message as input for row merging. This improves row merging performance by 10x. For 10k rows, each with 100 cells where each cell is 100 bytes and in groups of 100 rows per ReadRowsResponse, cProfile showed a 10x improvement: old: 124266037 function calls in 68.208 seconds new: 13042837 function calls in 7.787 seconds There are still a few more low hanging fruits to optimize performance and those will come in follow up PRs --- google/cloud/bigtable/row_data.py | 19 ++++++++++++------- tests/unit/test_row_data.py | 20 +++++++++++++------- tests/unit/test_table.py | 10 ++++++---- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/google/cloud/bigtable/row_data.py b/google/cloud/bigtable/row_data.py index 0c1565737..ab0358285 100644 --- a/google/cloud/bigtable/row_data.py +++ b/google/cloud/bigtable/row_data.py @@ -474,7 +474,11 @@ def _read_next(self): def _read_next_response(self): """Helper for :meth:`__iter__`.""" - return self.retry(self._read_next, on_error=self._on_error)() + resp_protoplus = self.retry(self._read_next, on_error=self._on_error)() + # unwrap the underlying protobuf, there is a significant amount of + # overhead that protoplus imposes for very little gain. The protos + # are not user visible, so we just use the raw protos for merging. + return data_messages_v2_pb2.ReadRowsResponse.pb(resp_protoplus) def __iter__(self): """Consume the ``ReadRowsResponse`` s from the stream. @@ -543,11 +547,12 @@ def _process_chunk(self, chunk): def _update_cell(self, chunk): if self._cell is None: qualifier = None - if "qualifier" in chunk: - qualifier = chunk.qualifier + if chunk.HasField("qualifier"): + qualifier = chunk.qualifier.value + family = None - if "family_name" in chunk: - family = chunk.family_name + if chunk.HasField("family_name"): + family = chunk.family_name.value self._cell = PartialCellData( chunk.row_key, @@ -577,8 +582,8 @@ def _validate_chunk_reset_row(self, chunk): # No reset with other keys _raise_if(chunk.row_key) - _raise_if("family_name" in chunk) - _raise_if("qualifier" in chunk) + _raise_if(chunk.HasField("family_name")) + _raise_if(chunk.HasField("qualifier")) _raise_if(chunk.timestamp_micros) _raise_if(chunk.labels) _raise_if(chunk.value_size) diff --git a/tests/unit/test_row_data.py b/tests/unit/test_row_data.py index 9b329dc9f..94a90aa24 100644 --- a/tests/unit/test_row_data.py +++ b/tests/unit/test_row_data.py @@ -637,15 +637,15 @@ def test_partial_rows_data__copy_from_previous_filled(): def test_partial_rows_data_valid_last_scanned_row_key_on_start(): client = _Client() - response = _ReadRowsResponseV2(chunks=(), last_scanned_row_key="2.AFTER") + response = _ReadRowsResponseV2([], last_scanned_row_key=b"2.AFTER") iterator = _MockCancellableIterator(response) client._data_stub = mock.MagicMock() client._data_stub.read_rows.side_effect = [iterator] request = object() yrd = _make_partial_rows_data(client._data_stub.read_rows, request) - yrd.last_scanned_row_key = "1.BEFORE" + yrd.last_scanned_row_key = b"1.BEFORE" _partial_rows_data_consume_all(yrd) - assert yrd.last_scanned_row_key == "2.AFTER" + assert yrd.last_scanned_row_key == b"2.AFTER" def test_partial_rows_data_invalid_empty_chunk(): @@ -666,6 +666,7 @@ def test_partial_rows_data_invalid_empty_chunk(): def test_partial_rows_data_state_cell_in_progress(): from google.cloud.bigtable_v2.services.bigtable import BigtableClient + from google.cloud.bigtable_v2.types import bigtable as messages_v2_pb2 LABELS = ["L1", "L2"] @@ -682,6 +683,9 @@ def test_partial_rows_data_state_cell_in_progress(): value=VALUE, labels=LABELS, ) + # _update_cell expects to be called after the protoplus wrapper has been + # shucked + chunk = messages_v2_pb2.ReadRowsResponse.CellChunk.pb(chunk) yrd._update_cell(chunk) more_cell_data = _ReadRowsResponseCellChunkPB(value=VALUE) @@ -1455,10 +1459,12 @@ def __init__(self, **kw): self.__dict__.update(kw) -class _ReadRowsResponseV2(object): - def __init__(self, chunks, last_scanned_row_key=""): - self.chunks = chunks - self.last_scanned_row_key = last_scanned_row_key +def _ReadRowsResponseV2(chunks, last_scanned_row_key=b""): + from google.cloud.bigtable_v2.types import bigtable as messages_v2_pb2 + + return messages_v2_pb2.ReadRowsResponse( + chunks=chunks, last_scanned_row_key=last_scanned_row_key + ) def _generate_cell_chunks(chunk_text_pbs): diff --git a/tests/unit/test_table.py b/tests/unit/test_table.py index 883f713d8..a89e02e8c 100644 --- a/tests/unit/test_table.py +++ b/tests/unit/test_table.py @@ -2206,10 +2206,12 @@ def next(self): __next__ = next -class _ReadRowsResponseV2(object): - def __init__(self, chunks, last_scanned_row_key=""): - self.chunks = chunks - self.last_scanned_row_key = last_scanned_row_key +def _ReadRowsResponseV2(chunks, last_scanned_row_key=b""): + from google.cloud.bigtable_v2.types import bigtable as messages_v2_pb2 + + return messages_v2_pb2.ReadRowsResponse( + chunks=chunks, last_scanned_row_key=last_scanned_row_key + ) def _TablePB(*args, **kw): From 1933125bf00345666ccdf1e0f05c945af18b49ce Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:59:49 -0700 Subject: [PATCH 11/11] chore(main): release 2.11.0 (#611) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 21 +++++++++++++++++++++ setup.py | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be1a397d8..38584bcbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,27 @@ [1]: https://pypi.org/project/google-cloud-bigtable/#history +## [2.11.0](https://github.com/googleapis/python-bigtable/compare/v2.10.1...v2.11.0) (2022-08-04) + + +### Features + +* add audience parameter ([a7a7699](https://github.com/googleapis/python-bigtable/commit/a7a76998fad3c12215527e4ebb517a1526cc152e)) +* add satisfies_pzs output only field ([#614](https://github.com/googleapis/python-bigtable/issues/614)) ([7dc1469](https://github.com/googleapis/python-bigtable/commit/7dc1469fef2dc38f1509b35a37e9c97381ab7601)) +* Add storage_utilization_gib_per_node to Autoscaling target ([a7a7699](https://github.com/googleapis/python-bigtable/commit/a7a76998fad3c12215527e4ebb517a1526cc152e)) +* Cloud Bigtable Undelete Table service and message proto files ([a7a7699](https://github.com/googleapis/python-bigtable/commit/a7a76998fad3c12215527e4ebb517a1526cc152e)) + + +### Bug Fixes + +* **deps:** require google-api-core>=1.32.0,>=2.8.0 ([a7a7699](https://github.com/googleapis/python-bigtable/commit/a7a76998fad3c12215527e4ebb517a1526cc152e)) +* require python 3.7+ ([#610](https://github.com/googleapis/python-bigtable/issues/610)) ([10d00f5](https://github.com/googleapis/python-bigtable/commit/10d00f5af5d5878c26529f5e48a5fb8d8385696d)) + + +### Performance Improvements + +* improve row merging ([#619](https://github.com/googleapis/python-bigtable/issues/619)) ([b4853e5](https://github.com/googleapis/python-bigtable/commit/b4853e59d0efd8a7b37f3fcb06b14dbd9f5d20a4)) + ## [2.10.1](https://github.com/googleapis/python-bigtable/compare/v2.10.0...v2.10.1) (2022-06-03) diff --git a/setup.py b/setup.py index 110101e5e..8087dddb6 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ name = "google-cloud-bigtable" description = "Google Cloud Bigtable API client library" -version = "2.10.1" +version = "2.11.0" # Should be one of: # 'Development Status :: 3 - Alpha' # 'Development Status :: 4 - Beta'