Skip to content

feat: implement native asyncio support via Cross-Sync#1509

Draft
sinhasubham wants to merge 4 commits intomainfrom
async_support_2
Draft

feat: implement native asyncio support via Cross-Sync#1509
sinhasubham wants to merge 4 commits intomainfrom
async_support_2

Conversation

@sinhasubham
Copy link
Contributor

Description: Native Asyncio Support
This PR introduces comprehensive, native asyncio support to the google-cloud-spanner library. It transitions the library into a "Cross-Sync" architecture, where the asynchronous implementation serves as the source of truth, and the synchronous implementation is automatically kept in parity.

Key Technical Changes
Core Library Porting

  • Asynchronous API: Introduced AsyncClient, AsyncInstance, and AsyncDatabase classes.
  • Session Management: Completely refactored pool.py to support asyncio. Replaced threading.Lock and queue.Queue with their asyncio counterparts via the CrossSync abstraction.
  • Transactions & Snapshots: Native async implementation of run_in_transaction, including robust retry logic for Aborted exceptions and proper lock management.

Verification & Testing
9 New System Tests: Created a dedicated async system test suite (tests/system/_async/) covering:
Rich data types (Timestamp, JSON, Protobuf, etc.)
Transaction retry loops
Partitioned DML operations
Session pool lifecycle
100% Pass Rate: All new async tests and existing sync tests pass reliably against the Spanner Emulator.
Mock Server Updates: Added 40+ mock server tests to verify specific async behaviors like result-set iteration and error handling.

@product-auto-label product-auto-label bot added size: xl Pull request size is extra large. api: spanner Issues related to the googleapis/python-spanner API. labels Mar 2, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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 integrates native asyncio capabilities into the google-cloud-spanner library. It introduces a novel 'Cross-Sync' architecture that treats the asynchronous code as the canonical implementation, automatically generating and synchronizing the synchronous API. This fundamental shift enhances the library's performance and responsiveness for concurrent operations, ensuring a robust and well-tested asynchronous experience for users.

Highlights

  • Native Asyncio Support: The library now features comprehensive, native asyncio support, enabling asynchronous operations throughout the google-cloud-spanner client.
  • Cross-Sync Architecture: A new 'Cross-Sync' architecture has been introduced, where the asynchronous implementation serves as the primary source of truth, and the synchronous implementation is automatically generated and kept in parity.
  • New Asynchronous Classes: Dedicated asynchronous classes such as AsyncClient, AsyncInstance, and AsyncDatabase have been added to provide an async-native API surface.
  • Refactored Session Management: The pool.py module has been completely refactored to support asyncio, replacing traditional threading primitives with their asyncio counterparts via the CrossSync abstraction.
  • Asynchronous Transaction Handling: The run_in_transaction method now includes a native asynchronous implementation, complete with robust retry logic for Aborted exceptions and proper lock management.
  • Extensive Verification and Testing: The changes are thoroughly tested with 9 new async system tests covering rich data types, transaction retry loops, partitioned DML, and session pool lifecycle, all passing reliably. Additionally, over 40 mock server tests were added for specific async behaviors.
Changelog
  • .cross_sync/README.md
    • Added documentation for the CrossSync library, detailing its shims, annotations, and code generation process.
  • .cross_sync/generate.py
    • Added a script for generating synchronous code from asynchronous code using CrossSync annotations.
  • .cross_sync/transformers.py
    • Added AST transformers (SymbolReplacer, AsyncToSync, RmAioFunctions, StripAsyncConditionalBranches, CrossSyncFileProcessor) to convert async Python code to sync.
  • google/cloud/aio/_cross_sync/init.py
    • Added __init__.py to expose the CrossSync module.
  • google/cloud/aio/_cross_sync/_decorators.py
    • Added decorator classes (AstDecorator, ConvertClass, Convert, Drop, Pytest, PytestFixture) to guide CrossSync transformations.
  • google/cloud/aio/_cross_sync/_mapping_meta.py
    • Added MappingMeta metaclass to enable runtime attribute mapping for CrossSync.
  • google/cloud/aio/_cross_sync/cross_sync.py
    • Added the core CrossSync class, providing async/sync shims, common function implementations, and decorators for code generation.
  • google/cloud/spanner.py
    • Reformatted imports for better readability.
  • google/cloud/spanner_admin_database_v1/init.py
    • Reordered imports for consistency and readability.
  • google/cloud/spanner_admin_database_v1/services/database_admin/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/client.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/pagers.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/transports/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/types/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/types/backup.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/types/backup_schedule.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/types/common.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/pagers.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/types/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/types/common.py
    • Reordered imports for consistency.
  • google/cloud/spanner_admin_instance_v1/types/spanner_instance_admin.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/_helpers.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/batch_dml_executor.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/client_side_statement_executor.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/client_side_statement_parser.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/connection.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/cursor.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/parse_utils.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/partition_helper.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/transaction_helper.py
    • Reordered imports for consistency.
  • google/cloud/spanner_dbapi/types.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/init.py
    • Added AsyncClient and async pool types to __all__.
    • Imported new async modules for client, database, instance, and pool.
  • google/cloud/spanner_v1/_async/_helpers.py
    • Added new helper functions for async operations, including retry logic for Aborted exceptions.
  • google/cloud/spanner_v1/_async/batch.py
    • Added asynchronous Batch and MutationGroups classes for batched writes, generated via CrossSync.
  • google/cloud/spanner_v1/_async/client.py
    • Added asynchronous Client class, generated via CrossSync, for interacting with Cloud Spanner API.
  • google/cloud/spanner_v1/_async/database.py
    • Added asynchronous Database class, generated via CrossSync, for managing Spanner databases.
  • google/cloud/spanner_v1/_async/database_sessions_manager.py
    • Added asynchronous DatabaseSessionsManager class, generated via CrossSync, for managing database sessions.
  • google/cloud/spanner_v1/_async/instance.py
    • Added asynchronous Instance class, generated via CrossSync, for managing Spanner instances.
  • google/cloud/spanner_v1/_async/pool.py
    • Added asynchronous session pool implementations (SessionCheckout, AbstractSessionPool, FixedSizePool, BurstyPool, PingingPool, TransactionPingingPool), generated via CrossSync.
  • google/cloud/spanner_v1/_async/session.py
    • Added asynchronous Session class, generated via CrossSync, for managing Spanner sessions.
  • google/cloud/spanner_v1/_async/snapshot.py
    • Added asynchronous Snapshot class, generated via CrossSync, for read-only queries.
  • google/cloud/spanner_v1/_async/streamed.py
    • Added asynchronous StreamedResultSet class, generated via CrossSync, for processing streaming results.
  • google/cloud/spanner_v1/_async/transaction.py
    • Added asynchronous Transaction class, generated via CrossSync, for read-write transactions.
  • google/cloud/spanner_v1/_helpers.py
    • Modified _datetime_to_rfc3339 and _datetime_to_rfc3339_nanoseconds to handle naive datetimes.
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/_opentelemetry_tracing.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/backup.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/batch.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Removed TODO comments related to span events per mutation.
  • google/cloud/spanner_v1/client.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Removed note about _http argument.
    • Adjusted _EMULATOR_HOST_HTTP_SCHEME formatting.
  • google/cloud/spanner_v1/data_types.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/database.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Replaced threading.local() with CrossSync._Sync_Impl.Local().
    • Adjusted _fill_pool to use asyncio.get_running_loop for async binding.
    • Updated _check_ddl_statements for clarity.
  • google/cloud/spanner_v1/database_sessions_manager.py
    • Added CrossSync generation header.
    • Replaced threading.Lock and threading.Event with CrossSync._Sync_Impl counterparts.
    • Adjusted _maintain_multiplexed_session to use CrossSync._Sync_Impl.sleep.
  • google/cloud/spanner_v1/instance.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Updated _type_string_to_type_pb to use Empty.
  • google/cloud/spanner_v1/keyset.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/merged_result_set.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/metrics/metrics_exporter.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/metrics/metrics_interceptor.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/metrics/metrics_tracer.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/metrics/metrics_tracer_factory.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/param_types.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/pool.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Replaced queue.LifoQueue and queue.PriorityQueue with CrossSync._Sync_Impl counterparts.
    • Updated ping and get methods to use CrossSync._Sync_Impl.queue_get and CrossSync._Sync_Impl.queue_put.
  • google/cloud/spanner_v1/services/spanner/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/services/spanner/async_client.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/services/spanner/client.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/services/spanner/pagers.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/services/spanner/transports/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/services/spanner/transports/base.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/services/spanner/transports/grpc.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/services/spanner/transports/grpc_asyncio.py
    • Modified intercept_unary_unary to remove await.
    • Commented out interceptor append for _grpc_channel.
  • google/cloud/spanner_v1/services/spanner/transports/rest.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/services/spanner/transports/rest_base.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/session.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Updated run_in_transaction to use CrossSync._Sync_Impl.run_if_async.
  • google/cloud/spanner_v1/snapshot.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Updated _restart_on_unavailable to use CrossSync._Sync_Impl.run_if_async.
  • google/cloud/spanner_v1/snapshot_helpers.py
    • Added new file for snapshot helper functions, generated via CrossSync.
  • google/cloud/spanner_v1/streamed.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Updated __iter__ to use __next__.
  • google/cloud/spanner_v1/table.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/testing/database_test.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/testing/interceptors.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/testing/mock_database_admin.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/testing/mock_spanner.py
    • Reordered imports for consistency.
    • Added clear_results method to SpannerServicer.
  • google/cloud/spanner_v1/testing/spanner_database_admin_pb2_grpc.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/testing/spanner_pb2_grpc.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/transaction.py
    • Added CrossSync generation header.
    • Reordered imports.
    • Updated _execute_request to use CrossSync._Sync_Impl.run_if_async.
  • google/cloud/spanner_v1/types/init.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/change_stream.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/commit_response.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/keys.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/location.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/mutation.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/query_plan.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/result_set.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/spanner.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/transaction.py
    • Reordered imports for consistency.
  • google/cloud/spanner_v1/types/type.py
    • Reordered imports for consistency.
  • noxfile.py
    • Added pytest-asyncio to SYSTEM_TEST_STANDARD_DEPENDENCIES.
  • test.py
    • Removed file.
  • tests/_builders.py
    • Reordered imports for consistency.
  • tests/_helpers.py
    • Reordered imports for consistency.
  • tests/mockserver_tests/mock_server_test_base.py
    • Added current_service ContextVar for tracking active Spanner service.
    • Added get_spanner_service helper function to retrieve the current Spanner service.
    • Added AsyncMockServerTestBase for asynchronous mock server tests, inheriting from unittest.IsolatedAsyncioTestCase.
    • Updated add_error, add_result, add_execute_streaming_sql_results to use get_spanner_service.
    • Modified MockServerTestBase to use setUpClass/tearDownClass and setUp/tearDown for pytest compatibility.
    • Added clear_results to MockServerTestBase.tearDown.
  • tests/mockserver_tests/test_aborted_transaction.py
    • Reordered imports for consistency.
  • tests/mockserver_tests/test_basics.py
    • Reordered imports for consistency.
  • tests/mockserver_tests/test_dbapi_autocommit.py
    • Modified setUp to use super().setUp().
  • tests/mockserver_tests/test_dbapi_isolation_level.py
    • Modified setUp to use super().setUp().
  • tests/mockserver_tests/test_request_id_header.py
    • Added super().tearDown() call.
  • tests/mockserver_tests/test_tags.py
    • Modified setUp to use super().setUp().
  • tests/system/_async/conftest.py
    • Added new file for async system test fixtures, including spanner_client, instance_operation_timeout, database_operation_timeout, shared_instance_id, database_dialect, proto_descriptor_file, instance_configs, instance_config, shared_instance, shared_database, databases_to_delete, and not_postgres.
  • tests/system/_async/pytest.ini
    • Added new file for pytest async configuration, setting asyncio_mode = auto and asyncio_default_test_loop_scope = session.
  • tests/system/_async/test_database_api.py
    • Added new file for async database API system tests, covering table creation, listing databases, batch inserts, transactions, and partitioned DML.
  • tests/system/_helpers.py
    • Reordered imports for consistency.
  • tests/system/_sample_data.py
    • Reordered imports for consistency.
    • Added ALL_TYPES_TABLE, ALL_TYPES_COLUMNS, AllTypesRowData, and EMULATOR_ALL_TYPES_ROWDATA for comprehensive data type testing.
  • tests/system/conftest.py
    • Reordered imports for consistency.
  • tests/system/test_backup_api.py
    • Reordered imports for consistency.
  • tests/system/test_database_api.py
    • Reordered imports for consistency.
  • tests/system/test_dbapi.py
    • Reordered imports for consistency.
  • tests/system/test_instance_api.py
    • Reordered imports for consistency.
  • tests/system/test_metrics.py
    • Reordered imports for consistency.
  • tests/system/test_observability_options.py
    • Reordered imports for consistency.
  • tests/system/test_session_api.py
    • Reordered imports for consistency.
  • tests/system/test_table_api.py
    • Reordered imports for consistency.
  • tests/system/utils/clear_streaming.py
    • Reordered imports for consistency.
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 significant architectural change with the new Cross-Sync mechanism for generating synchronous code from an asynchronous source. The implementation of the code generation and the new async components is well-structured. However, I've identified a critical issue in an async transport interceptor where a missing await will likely cause runtime errors. Additionally, there are opportunities for improvement by refactoring duplicated code in the AST transformers, enhancing the robustness of AST checks, and correcting some documentation typos and inaccuracies. Addressing these points will further strengthen this excellent contribution towards native asyncio support.

},
)
response = await continuation(client_call_details, request)
response = continuation(client_call_details, request)
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 await keyword was removed here, but continuation returns an awaitable. Without await, response will be a coroutine object, and subsequent calls like response.trailing_metadata() will fail. This should be response = await continuation(client_call_details, request).

Suggested change
response = continuation(client_call_details, request)
response = await continuation(client_call_details, request)


self._interceptor = _LoggingClientAIOInterceptor()
self._grpc_channel._unary_unary_interceptors.append(self._interceptor)
# self._grpc_channel._unary_unary_interceptors.append(self._interceptor)
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The logging interceptor is being commented out. Is this intentional? If so, it should be documented why. If it's because of the issue with await continuation(...), the fix should be to add the await back, not to disable the interceptor.

- `CrossSync.gather_partials()`
- `CrossSync.wait()`
- `CrossSync.condition_wait()`
- `CrossSync,event_wait()`
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

There's a typo here. It should be CrossSync.event_wait() instead of CrossSync,event_wait().

Suggested change
- `CrossSync,event_wait()`
- `CrossSync.event_wait()`

### Code Generation

Generation can be initiated using `nox -s generate_sync`
from the root of the project. This will find all classes with the `__CROSS_SYNC_OUTPUT__ = "path/to/output"`
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

There's a trailing space in the code example which could be confusing.

Suggested change
from the root of the project. This will find all classes with the `__CROSS_SYNC_OUTPUT__ = "path/to/output"`
from the root of the project. This will find all classes with the `__CROSS_SYNC_OUTPUT__ = "path/to/output"`

## Architecture

CrossSync is made up of two parts:
- the runtime shims and annotations live in `/google/cloud/bigtable/_cross_sync`
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 path seems incorrect. Based on the file structure in this PR, it should probably be /google/cloud/aio/_cross_sync.

Suggested change
- the runtime shims and annotations live in `/google/cloud/bigtable/_cross_sync`
- the runtime shims and annotations live in `/google/cloud/aio/_cross_sync`

file_transformer = CrossSyncFileProcessor()
# run each file through ast transformation to find all annotated classes
for file_path in files:
ast_tree = ast.parse(open(file_path, encoding="utf-8-sig").read())
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 safer to use a with statement when opening files. This ensures the file is closed even if an error occurs during parsing. For example:

with open(file_path, encoding="utf-8-sig") as f:
    ast_tree = ast.parse(f.read())

Comment on lines +66 to +76
def visit_ImportFrom(self, node):
if node.module:
if "_async" in node.module: node.module = node.module.replace("._async", "").replace("_async.", "").replace("_async", "")
if "async_client" in node.module: node.module = node.module.replace("async_client", "client")
# Also replace AsyncClient with Client in the names!
for alias in node.names:
if "AsyncClient" in alias.name:
alias.name = alias.name.replace("AsyncClient", "Client")
if alias.name == "AsyncRetry":
alias.name = "Retry"
return self.generic_visit(node)
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 visit_ImportFrom method is duplicated in SymbolReplacer, AsyncToSync (line 146), and CrossSyncFileProcessor (line 363). This logic should be refactored into a single place, for example, a new NodeTransformer class, to avoid inconsistencies and improve maintainability.

if not isinstance(root_attr, ast.Attribute):
raise ValueError("Unexpected decorator format")
# extract the module and decorator names
if "CrossSync" in ast.dump(root_attr):
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Using ast.dump to check for the decorator's origin can be brittle as it relies on string representation. It's more robust to traverse the AST to check for the CrossSync attribute. This same pattern is used on line 258. Consider creating a helper function for this check.

- Port TLS/mTLS and experimental host support to AsyncClient
- Port enable_interceptors_in_tests to AsyncInstance.database
- Regenerate synchronous code via CrossSync
- Fix noxfile.py for pytest-asyncio compatibility and test isolation
- Add comprehensive asynchronous system tests
@chalmerlowe
Copy link
Contributor

📢 Migration Notice: 📢 This library is moving to the google-cloud-python monorepo soon.

We kept this PR open due to recent activity. We would like to finalize this PR so it can be merged if it is critical.

If we don't hear from the PR author, we will close this PR in the next few days. The PR can then be re-opened in the monorepo once the migration is complete and work can continue there.

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

Labels

api: spanner Issues related to the googleapis/python-spanner API. size: xl Pull request size is extra large.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants