Skip to content

Commit

Permalink
chore(docs): add basic samples for async data client (#940)
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-sanche committed Mar 18, 2024
1 parent 42a2976 commit 390b059
Show file tree
Hide file tree
Showing 40 changed files with 1,006 additions and 192 deletions.
4 changes: 2 additions & 2 deletions samples/beam/noxfile_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
TEST_CONFIG_OVERRIDE = {
# You can opt out from the test for specific Python versions.
"ignored_versions": [
"2.7", # not supported
"3.10", # Beam wheels not yet released for Python 3.10
"3.7", # Beam no longer supports Python 3.7 for new releases
"3.12", # Beam not yet supported for Python 3.12
],
# Old samples are opted out of enforcing Python type hints
# All new samples should feature them
Expand Down
2 changes: 1 addition & 1 deletion samples/beam/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pytest==8.0.0
pytest==7.4.4
2 changes: 1 addition & 1 deletion samples/beam/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
apache-beam==2.53.0
apache-beam==2.54.0
google-cloud-bigtable==2.22.0
google-cloud-core==2.4.1
140 changes: 140 additions & 0 deletions samples/hello/async_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env python

# Copyright 2024 Google Inc.
#
# 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.

"""Demonstrates how to connect to Cloud Bigtable and run some basic operations with the async APIs
Prerequisites:
- Create a Cloud Bigtable instance.
https://cloud.google.com/bigtable/docs/creating-instance
- Set your Google Application Default Credentials.
https://developers.google.com/identity/protocols/application-default-credentials
"""

import argparse
import asyncio

# [START bigtable_async_hw_imports]
from google.cloud import bigtable
from google.cloud.bigtable.data import row_filters
from google.cloud.bigtable.data import RowMutationEntry
from google.cloud.bigtable.data import SetCell
from google.cloud.bigtable.data import ReadRowsQuery

# [END bigtable_async_hw_imports]


async def main(project_id, instance_id, table_id):
# [START bigtable_async_hw_connect]
client = bigtable.data.BigtableDataClientAsync(project=project_id)
table = client.get_table(instance_id, table_id)
# [END bigtable_async_hw_connect]

# [START bigtable_async_hw_create_table]
from google.cloud.bigtable import column_family

# the async client only supports the data API. Table creation as an admin operation
# use admin client to create the table
print("Creating the {} table.".format(table_id))
admin_client = bigtable.Client(project=project_id, admin=True)
admin_instance = admin_client.instance(instance_id)
admin_table = admin_instance.table(table_id)

print("Creating column family cf1 with Max Version GC rule...")
# Create a column family with GC policy : most recent N versions
# Define the GC policy to retain only the most recent 2 versions
max_versions_rule = column_family.MaxVersionsGCRule(2)
column_family_id = "cf1"
column_families = {column_family_id: max_versions_rule}
if not admin_table.exists():
admin_table.create(column_families=column_families)
else:
print("Table {} already exists.".format(table_id))
# [END bigtable_async_hw_create_table]

# [START bigtable_async_hw_write_rows]
print("Writing some greetings to the table.")
greetings = ["Hello World!", "Hello Cloud Bigtable!", "Hello Python!"]
mutations = []
column = "greeting"
for i, value in enumerate(greetings):
# Note: This example uses sequential numeric IDs for simplicity,
# but this can result in poor performance in a production
# application. Since rows are stored in sorted order by key,
# sequential keys can result in poor distribution of operations
# across nodes.
#
# For more information about how to design a Bigtable schema for
# the best performance, see the documentation:
#
# https://cloud.google.com/bigtable/docs/schema-design
row_key = "greeting{}".format(i).encode()
row_mutation = RowMutationEntry(
row_key, SetCell(column_family_id, column, value)
)
mutations.append(row_mutation)
await table.bulk_mutate_rows(mutations)
# [END bigtable_async_hw_write_rows]

# [START bigtable_async_hw_create_filter]
# Create a filter to only retrieve the most recent version of the cell
# for each column across entire row.
row_filter = row_filters.CellsColumnLimitFilter(1)
# [END bigtable_async_hw_create_filter]

# [START bigtable_async_hw_get_with_filter]
# [START bigtable_async_hw_get_by_key]
print("Getting a single greeting by row key.")
key = "greeting0".encode()

row = await table.read_row(key, row_filter=row_filter)
cell = row.cells[0]
print(cell.value.decode("utf-8"))
# [END bigtable_async_hw_get_by_key]
# [END bigtable_async_hw_get_with_filter]

# [START bigtable_async_hw_scan_with_filter]
# [START bigtable_async_hw_scan_all]
print("Scanning for all greetings:")
query = ReadRowsQuery(row_filter=row_filter)
async for row in await table.read_rows_stream(query):
cell = row.cells[0]
print(cell.value.decode("utf-8"))
# [END bigtable_async_hw_scan_all]
# [END bigtable_async_hw_scan_with_filter]

# [START bigtable_async_hw_delete_table]
# the async client only supports the data API. Table deletion as an admin operation
# use admin client to create the table
print("Deleting the {} table.".format(table_id))
admin_table.delete()
# [END bigtable_async_hw_delete_table]


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument("project_id", help="Your Cloud Platform project ID.")
parser.add_argument(
"instance_id", help="ID of the Cloud Bigtable instance to connect to."
)
parser.add_argument(
"--table", help="Table to create and destroy.", default="Hello-Bigtable"
)

args = parser.parse_args()
asyncio.run(main(args.project_id, args.instance_id, args.table))
39 changes: 39 additions & 0 deletions samples/hello/async_main_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2024 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import random
import asyncio

from async_main import main

PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"]
BIGTABLE_INSTANCE = os.environ["BIGTABLE_INSTANCE"]
TABLE_NAME_FORMAT = "hello-world-test-{}"
TABLE_NAME_RANGE = 10000


def test_async_main(capsys):
table_name = TABLE_NAME_FORMAT.format(random.randrange(TABLE_NAME_RANGE))

asyncio.run(main(PROJECT, BIGTABLE_INSTANCE, table_name))

out, _ = capsys.readouterr()
assert "Creating the {} table.".format(table_name) in out
assert "Writing some greetings to the table." in out
assert "Getting a single greeting by row key." in out
assert "Hello World!" in out
assert "Scanning for all greetings" in out
assert "Hello Cloud Bigtable!" in out
assert "Deleting the {} table.".format(table_name) in out
4 changes: 2 additions & 2 deletions samples/hello/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
Prerequisites:
- Create a Cloud Bigtable cluster.
https://cloud.google.com/bigtable/docs/creating-cluster
- Create a Cloud Bigtable instance.
https://cloud.google.com/bigtable/docs/creating-instance
- Set your Google Application Default Credentials.
https://developers.google.com/identity/protocols/application-default-credentials
"""
Expand Down
15 changes: 8 additions & 7 deletions samples/hello/noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def blacken(session: nox.sessions.Session) -> None:
# format = isort + black
#


@nox.session
def format(session: nox.sessions.Session) -> None:
"""
Expand Down Expand Up @@ -187,7 +188,9 @@ def _session_tests(
session: nox.sessions.Session, post_install: Callable = None
) -> None:
# check for presence of tests
test_list = glob.glob("**/*_test.py", recursive=True) + glob.glob("**/test_*.py", recursive=True)
test_list = glob.glob("**/*_test.py", recursive=True) + glob.glob(
"**/test_*.py", recursive=True
)
test_list.extend(glob.glob("**/tests", recursive=True))

if len(test_list) == 0:
Expand All @@ -209,9 +212,7 @@ def _session_tests(

if os.path.exists("requirements-test.txt"):
if os.path.exists("constraints-test.txt"):
session.install(
"-r", "requirements-test.txt", "-c", "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:
Expand All @@ -224,9 +225,9 @@ def _session_tests(
post_install(session)

if "pytest-parallel" in packages:
concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto'])
concurrent_args.extend(["--workers", "auto", "--tests-per-worker", "auto"])
elif "pytest-xdist" in packages:
concurrent_args.extend(['-n', 'auto'])
concurrent_args.extend(["-n", "auto"])

session.run(
"pytest",
Expand Down Expand Up @@ -256,7 +257,7 @@ def py(session: nox.sessions.Session) -> None:


def _get_repo_root() -> Optional[str]:
""" Returns the root folder of the project. """
"""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):
Expand Down
2 changes: 1 addition & 1 deletion samples/hello/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pytest==8.0.0
pytest==7.4.4
2 changes: 1 addition & 1 deletion samples/hello/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
google-cloud-bigtable==2.22.0
google-cloud-bigtable==2.23.0
google-cloud-core==2.4.1
2 changes: 1 addition & 1 deletion samples/hello_happybase/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pytest==8.0.0
pytest==7.4.4
2 changes: 1 addition & 1 deletion samples/instanceadmin/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pytest==8.0.0
pytest==7.4.4
2 changes: 1 addition & 1 deletion samples/metricscaler/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pytest==8.0.0
pytest==7.4.4
mock==5.1.0
google-cloud-testutils
2 changes: 1 addition & 1 deletion samples/quickstart/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pytest==8.0.0
pytest==7.4.4
2 changes: 1 addition & 1 deletion samples/quickstart_happybase/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pytest==8.0.0
pytest==7.4.4

0 comments on commit 390b059

Please sign in to comment.