Skip to content

Conversation

@chandra-siri
Copy link
Collaborator

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> 🦕

@product-auto-label product-auto-label bot added size: m Pull request size is medium. api: storage Issues related to the googleapis/python-storage API. samples Issues that are directly related to samples. labels Jan 20, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @chandra-siri, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new Python script designed to serve as a reproduction example for performing asynchronous bidirectional writes to Google Cloud Storage. The script leverages the underlying GAPIC client (_storage_v2) to demonstrate how to initiate an appendable write stream and subsequently send data in chunks. This provides a concrete, runnable example for developers working with advanced asynchronous I/O patterns for large object uploads to Cloud Storage.

Highlights

  • New Reproduction Script: A new Python script, bidi_writes_from_gapic.py, has been added to demonstrate asynchronous bidirectional writes to Google Cloud Storage.
  • GAPIC Client Usage: The script directly utilizes the google.cloud._storage_v2 (GAPIC) client for low-level gRPC communication to perform object writes.
  • Appendable Writes: It includes functionality to open a file for appendable writes, retrieving the object's generation and a write handle, and a framework for appending data in chunks.
  • Command-Line Arguments: The script is configurable via command-line arguments for specifying the target filename and controlling whether to skip the 'open' or 'append' operations.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a script for demonstrating bidi writes. The script appears to be a work in progress, containing significant amounts of commented-out code, hardcoded values, and deviations from standard Python practices. My review focuses on cleaning up the code, improving its reusability and robustness, and aligning it with best practices. A critical concern is the use of a private module (_storage_v2), which is not suitable for a public-facing sample.

import os
import time
import asyncio
from google.cloud import _storage_v2 as storage_v2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The script imports _storage_v2, which is a private module as indicated by the leading underscore. Public samples should only use the public API of the library. Please use the public google.cloud.storage module if possible. If this feature is only available in the private module, this script might not be suitable as a public sample.

Comment on lines +73 to +87
async for response in response_stream:
# print("stream count:", count, "*" * 20)
# print(response)
# print("time elapsed", time_elapsed)
# print("stream count:", count, "*" * 20)
if response.resource is not None and (generation is None):

generation = response.resource.generation

# print("genration = ", generation)
if response.write_handle is not None and (write_handle is None):
write_handle = response.write_handle.handle
# print("write_handle = ", write_handle)

# if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This block contains a significant amount of commented-out print statements and a stray # if which seems to be leftover from development. This dead code should be removed to improve readability and maintainability.

    async for response in response_stream:
        if response.resource is not None and (generation is None):
            generation = response.resource.generation
        if response.write_handle is not None and (write_handle is None):
            write_handle = response.write_handle.handle

Comment on lines +170 to +181
# if not args.skip_open:
generation, write_handle = await open_file(filename, storage_async_client)
print("Opend file for writing, gen:", generation)
# if not args.skip_append:
# if generation is None:
# print("generation is none requesting it")
# object_metadata = await get_object(
# bucket_fullname=BUCKET_FULLNAME, object_name=filename
# )
# generation = object_metadata.generation
# print("generation is ", generation)
# await append_to_file(filename, generation, storage_async_client, data, write_handle)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This section contains a lot of commented-out code which seems to be the main logic for appending to the file. The script as it is only opens the file but doesn't perform the append operation. The commented-out if conditions for skip_open and skip_append also suggest that the script is intended to be more configurable than it currently is. This commented code should be cleaned up and the intended logic should be implemented.

Comment on lines +1 to +12
"""
async def open_file('file_name')
async def append_to_file('file_name')
async def close('')
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current block at the top of the file appears to be scratchpad notes rather than a proper module docstring. To improve clarity and adhere to Python conventions, it should be replaced with a concise docstring that explains the script's purpose.

"""A script to demonstrate bidi writes using the google-cloud-storage GAPIC client."""


# from async_get_object_metadata import get_object

BUCKET_NAME = "chandrasiri-rs"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The bucket name is hardcoded, which limits the script's reusability. Consider making it a command-line argument, similar to filename.

You can add the following to your ArgumentParser setup:

parser.add_argument("--bucket_name", required=True)

And then use args.bucket_name here.

Comment on lines +58 to +60
def request_generator():
for request in [first_request]:
yield request
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The request_generator function can be simplified. The for loop over a single-element list is redundant. A simple yield is sufficient.

    def request_generator():
        yield first_request

requests=request_generator(),
metadata=(
("x-goog-request-params", req_param),
("x-goog-api-client", "gcloud-python-local/3.8.0"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The x-goog-api-client header is hardcoded. This is likely for specific debugging purposes. For a general-purpose script, it's better to remove this line and let the client library set its default user-agent header.

Comment on lines +151 to +159
count = 0
prev_time = time.monotonic_ns()
total_time = 0
async for response in append_stream:
end_time = time.monotonic_ns()
elapsed_time = end_time - prev_time
total_time += elapsed_time
print(f"Response count: {count}:", response)
count += 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block appears to be for debugging and performance measurement, printing each response and timing information. For a clean, reusable script, this should be removed. If you just need to consume the stream, an empty async for loop is sufficient.

    async for _ in append_stream:
        pass

count += 1


async def main():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To make this function more self-contained and easier to test, it should receive the parsed command-line arguments as a parameter instead of relying on a global args variable.

async def main(args):

Comment on lines +184 to +191
parser = argparse.ArgumentParser()
parser.add_argument("--filename", required=True)
parser.add_argument("--skip_open", action="store_true")
parser.add_argument("--skip_append", action="store_true")
args = parser.parse_args()
# print(args.skip_open, args.skip_append)
# print("yo")
asyncio.run(main())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's a Python best practice to place script execution logic within an if __name__ == "__main__": block. This prevents the code from being executed when the module is imported elsewhere. This also provides a good place to call main with the parsed args.

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--filename", required=True)
    parser.add_argument("--skip_open", action="store_true")
    parser.add_argument("--skip_append", action="store_true")
    args = parser.parse_args()
    asyncio.run(main(args))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: storage Issues related to the googleapis/python-storage API. samples Issues that are directly related to samples. size: m Pull request size is medium.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant