google-cloud-spanner 3.63.0__tar.gz → 3.64.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {google_cloud_spanner-3.63.0/google_cloud_spanner.egg-info → google_cloud_spanner-3.64.0}/PKG-INFO +4 -3
- google_cloud_spanner-3.64.0/google/cloud/aio/_cross_sync/__init__.py +19 -0
- google_cloud_spanner-3.64.0/google/cloud/aio/_cross_sync/_decorators.py +467 -0
- google_cloud_spanner-3.64.0/google/cloud/aio/_cross_sync/_mapping_meta.py +65 -0
- google_cloud_spanner-3.64.0/google/cloud/aio/_cross_sync/cross_sync.py +424 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner.py +13 -12
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/__init__.py +74 -68
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/gapic_version.py +1 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py +74 -49
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/client.py +77 -54
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/pagers.py +25 -21
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/__init__.py +1 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py +34 -19
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py +27 -23
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py +28 -24
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py +49 -64
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py +13 -13
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/__init__.py +2 -7
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/backup.py +18 -16
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/backup_schedule.py +3 -4
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/common.py +4 -4
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py +8 -6
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/__init__.py +46 -48
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/gapic_version.py +1 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py +66 -43
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py +69 -48
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/pagers.py +18 -16
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/__init__.py +1 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py +29 -16
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py +28 -26
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py +29 -27
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py +60 -75
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py +8 -10
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/types/__init__.py +1 -5
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/types/common.py +2 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/types/spanner_instance_admin.py +109 -18
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/__init__.py +28 -30
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/_helpers.py +0 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/batch_dml_executor.py +19 -4
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/connection.py +51 -14
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/cursor.py +57 -31
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/parse_utils.py +3 -2
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/partition_helper.py +3 -4
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/transaction_helper.py +7 -6
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/types.py +3 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/version.py +1 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/__init__.py +66 -53
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/_helpers.py +131 -0
- {google_cloud_spanner-3.63.0/google/cloud/spanner_v1 → google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async}/batch.py +102 -47
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/client.py +708 -0
- {google_cloud_spanner-3.63.0/google/cloud/spanner_v1 → google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async}/database.py +380 -406
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/database_sessions_manager.py +243 -0
- {google_cloud_spanner-3.63.0/google/cloud/spanner_v1 → google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async}/instance.py +68 -36
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/pool.py +974 -0
- {google_cloud_spanner-3.63.0/google/cloud/spanner_v1 → google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async}/session.py +105 -67
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/snapshot.py +857 -0
- {google_cloud_spanner-3.63.0/google/cloud/spanner_v1 → google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async}/streamed.py +32 -20
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/testing/database_test.py +207 -0
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/testing/interceptors.py +107 -0
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/transaction.py +881 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/_helpers.py +190 -27
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/_opentelemetry_tracing.py +9 -10
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/backup.py +7 -6
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/batch.py +419 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/client.py +95 -92
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/data_types.py +3 -2
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/database.py +1869 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/database_sessions_manager.py +53 -109
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/gapic_version.py +1 -1
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/instance.py +692 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/keyset.py +3 -5
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/merged_result_set.py +9 -6
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_capture.py +17 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_exporter.py +59 -34
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_interceptor.py +4 -6
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_tracer.py +5 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_tracer_factory.py +17 -18
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/spanner_metrics_tracer_factory.py +9 -6
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/param_types.py +7 -5
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/pool.py +192 -194
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/async_client.py +27 -22
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/client.py +34 -27
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/pagers.py +8 -7
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/__init__.py +1 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/base.py +23 -13
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/grpc.py +19 -14
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/grpc_asyncio.py +20 -15
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/rest.py +24 -22
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/rest_base.py +11 -11
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/session.py +608 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/snapshot.py +221 -401
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/snapshot_helpers.py +730 -0
- google_cloud_spanner-3.64.0/google/cloud/spanner_v1/streamed.py +367 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/table.py +1 -5
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/database_test.py +30 -19
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/interceptors.py +3 -2
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/mock_database_admin.py +1 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/mock_spanner.py +60 -7
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/spanner_database_admin_pb2_grpc.py +1 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/spanner_pb2_grpc.py +1 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/transaction.py +94 -187
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/__init__.py +7 -30
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/change_stream.py +16 -15
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/commit_response.py +15 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/keys.py +1 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/location.py +10 -11
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/mutation.py +2 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/query_plan.py +2 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/result_set.py +14 -2
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/spanner.py +105 -38
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/transaction.py +44 -25
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/type.py +2 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0/google_cloud_spanner.egg-info}/PKG-INFO +4 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/SOURCES.txt +42 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/setup.py +3 -2
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/_builders.py +59 -13
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/_fixtures.py +0 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/_helpers.py +8 -4
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/mock_server_test_base.py +203 -37
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_aborted_transaction.py +7 -6
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_basics.py +5 -9
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_dbapi_autocommit.py +4 -5
- google_cloud_spanner-3.64.0/tests/mockserver_tests/test_dbapi_inline_begin.py +906 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_dbapi_isolation_level.py +19 -44
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_request_id_header.py +8 -7
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_tags.py +10 -13
- google_cloud_spanner-3.64.0/tests/system/_async/__init__.py +0 -0
- google_cloud_spanner-3.64.0/tests/system/_async/conftest.py +207 -0
- google_cloud_spanner-3.64.0/tests/system/_async/pytest.ini +4 -0
- google_cloud_spanner-3.64.0/tests/system/_async/test_database_api.py +194 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/_helpers.py +11 -5
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/_sample_data.py +79 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/conftest.py +6 -2
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_backup_api.py +3 -2
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_database_api.py +6 -9
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_dbapi.py +153 -94
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_instance_api.py +0 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_session_api.py +26 -22
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_table_api.py +2 -1
- {google_cloud_spanner-3.63.0/tests/unit → google_cloud_spanner-3.64.0/tests/system}/testdata/singer_pb2.py +1 -0
- google_cloud_spanner-3.64.0/tests/system/utils/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/utils/clear_streaming.py +3 -5
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/utils/populate_streaming.py +12 -13
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/utils/scrub_instances.py +1 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_batch.py +301 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_client.py +780 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_client_extra.py +225 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_database.py +4361 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_database_extra.py +513 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_helpers_extra.py +143 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_instance_extra.py +207 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_pool.py +954 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_session.py +2822 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_sessions_manager_extra.py +218 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_snapshot.py +1142 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_streamed.py +1336 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_streamed_extra.py +114 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_transaction.py +1596 -0
- google_cloud_spanner-3.64.0/tests/unit/_async/test_transaction_extra.py +235 -0
- google_cloud_spanner-3.64.0/tests/unit/conftest.py +18 -0
- google_cloud_spanner-3.64.0/tests/unit/gapic/conftest.py +20 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_admin_database_v1/test_database_admin.py +708 -488
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_admin_instance_v1/test_instance_admin.py +592 -392
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_v1/test_spanner.py +324 -280
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_checksum.py +16 -8
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_connect.py +13 -9
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_connection.py +32 -10
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_cursor.py +16 -11
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_globals.py +1 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_parse_utils.py +4 -5
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_parser.py +28 -29
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_transaction_helper.py +5 -7
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_types.py +0 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test__helpers.py +104 -94
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test__opentelemetry_tracing.py +7 -10
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_atomic_counter.py +1 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_backup.py +24 -19
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_batch.py +39 -27
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_client.py +41 -29
- google_cloud_spanner-3.64.0/tests/unit/test_client_context.py +441 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_database.py +153 -86
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_database_session_manager.py +62 -16
- google_cloud_spanner-3.64.0/tests/unit/test_decorators_extra.py +350 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_exceptions.py +1 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_instance.py +72 -49
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_keyset.py +1 -2
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_merged_result_set.py +3 -4
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics.py +34 -12
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_capture.py +3 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_concurrency.py +2 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_exporter.py +18 -21
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_interceptor.py +3 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_tracer.py +4 -3
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_tracer_factory.py +3 -9
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_param_types.py +10 -10
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_pool.py +739 -293
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_session.py +59 -46
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_snapshot.py +58 -52
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_spanner.py +42 -25
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_spanner_metrics_tracer_factory.py +1 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_streamed.py +16 -23
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_table.py +2 -6
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_transaction.py +34 -26
- {google_cloud_spanner-3.63.0/tests/system → google_cloud_spanner-3.64.0/tests/unit}/testdata/singer_pb2.py +1 -0
- google_cloud_spanner-3.63.0/tests/unit/conftest.py +0 -27
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/LICENSE +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/MANIFEST.in +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/README.rst +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/gapic_metadata.json +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/py.typed +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/database_admin/__init__.py +1 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/gapic_metadata.json +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/py.typed +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/__init__.py +1 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/checksum.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/client_side_statement_executor.py +6 -6
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/client_side_statement_parser.py +2 -2
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/exceptions.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/parsed_statement.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/parser.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/utils.py +0 -0
- {google_cloud_spanner-3.63.0/google/cloud/spanner_v1 → google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async}/testing/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/exceptions.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/gapic_metadata.json +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/constants.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/py.typed +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/request_id_header.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/__init__.py +1 -1
- {google_cloud_spanner-3.63.0/tests/mockserver_tests → google_cloud_spanner-3.64.0/google/cloud/spanner_v1/testing}/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/dependency_links.txt +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/not-zip-safe +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/requires.txt +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/top_level.txt +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/setup.cfg +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/__init__.py +0 -0
- {google_cloud_spanner-3.63.0/tests/system → google_cloud_spanner-3.64.0/tests/mockserver_tests}/__init__.py +0 -0
- {google_cloud_spanner-3.63.0/tests/system/utils → google_cloud_spanner-3.64.0/tests/system}/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_metrics.py +1 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_observability_options.py +10 -10
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_streaming_chunking.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/testdata/descriptors.pb +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/testdata/singer.proto +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/utils/streaming_utils.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_admin_database_v1/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_admin_instance_v1/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_v1/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/__init__.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test__helpers.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_batch_dml_executor.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_client_side_statement_executor.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_utils.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/streaming-read-acceptance-test.json +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_datatypes.py +1 -1
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_packaging.py +0 -0
- {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/testdata/singer.proto +0 -0
{google_cloud_spanner-3.63.0/google_cloud_spanner.egg-info → google_cloud_spanner-3.64.0}/PKG-INFO
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-cloud-spanner
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.64.0
|
|
4
4
|
Summary: Google Cloud Spanner API client library
|
|
5
|
-
Home-page: https://github.com/googleapis/python-spanner
|
|
5
|
+
Home-page: https://github.com/googleapis/google-cloud-python/tree/main/packages/google-cloud-spanner
|
|
6
6
|
Author: Google LLC
|
|
7
7
|
Author-email: googleapis-packages@google.com
|
|
8
8
|
License: Apache 2.0
|
|
@@ -12,6 +12,7 @@ Classifier: Intended Audience :: Developers
|
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: Programming Language :: Python
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -19,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Operating System :: OS Independent
|
|
21
22
|
Classifier: Topic :: Internet
|
|
22
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.8
|
|
23
24
|
License-File: LICENSE
|
|
24
25
|
Requires-Dist: google-api-core[grpc]!=2.0.*,!=2.1.*,!=2.10.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,<3.0.0,>=1.34.0
|
|
25
26
|
Requires-Dist: google-cloud-core<3.0.0,>=1.4.4
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from .cross_sync import CrossSync
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"CrossSync",
|
|
19
|
+
]
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""
|
|
15
|
+
Contains a set of AstDecorator classes, which define the behavior of CrossSync decorators.
|
|
16
|
+
Each AstDecorator class is used through @CrossSync.<decorator_name>
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from typing import TYPE_CHECKING, Iterable
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
import ast
|
|
25
|
+
from typing import Any, Callable
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AstDecorator:
|
|
29
|
+
"""
|
|
30
|
+
Helper class for CrossSync decorators used for guiding ast transformations.
|
|
31
|
+
|
|
32
|
+
AstDecorators are accessed in two ways:
|
|
33
|
+
1. The decorations are used directly as method decorations in the async client,
|
|
34
|
+
wrapping existing classes and methods
|
|
35
|
+
2. The decorations are read back when processing the AST transformations when
|
|
36
|
+
generating sync code.
|
|
37
|
+
|
|
38
|
+
This class allows the same decorator to be used in both contexts.
|
|
39
|
+
|
|
40
|
+
Typically, AstDecorators act as a no-op in async code, and the arguments simply
|
|
41
|
+
provide configuration guidance for the sync code generation.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def decorator(cls, *args, **kwargs) -> Callable[..., Any]:
|
|
46
|
+
"""
|
|
47
|
+
Provides a callable that can be used as a decorator function in async code
|
|
48
|
+
|
|
49
|
+
AstDecorator.decorate is called by CrossSync when attaching decorators to
|
|
50
|
+
the CrossSync class.
|
|
51
|
+
|
|
52
|
+
This method creates a new instance of the class, using the arguments provided
|
|
53
|
+
to the decorator, and defers to the async_decorator method of the instance
|
|
54
|
+
to build the wrapper function.
|
|
55
|
+
|
|
56
|
+
Arguments:
|
|
57
|
+
*args: arguments to the decorator
|
|
58
|
+
**kwargs: keyword arguments to the decorator
|
|
59
|
+
"""
|
|
60
|
+
# decorators with no arguments will provide the function to be wrapped
|
|
61
|
+
# as the first argument. Pull it out if it exists
|
|
62
|
+
func = None
|
|
63
|
+
if len(args) == 1 and callable(args[0]):
|
|
64
|
+
func = args[0]
|
|
65
|
+
args = args[1:]
|
|
66
|
+
# create new AstDecorator instance from given decorator arguments
|
|
67
|
+
new_instance = cls(*args, **kwargs)
|
|
68
|
+
# build wrapper
|
|
69
|
+
wrapper = new_instance.async_decorator()
|
|
70
|
+
if wrapper is None:
|
|
71
|
+
# if no wrapper, return no-op decorator
|
|
72
|
+
return func or (lambda f: f)
|
|
73
|
+
elif func:
|
|
74
|
+
# if we can, return single wrapped function
|
|
75
|
+
return wrapper(func)
|
|
76
|
+
else:
|
|
77
|
+
# otherwise, return decorator function
|
|
78
|
+
return wrapper
|
|
79
|
+
|
|
80
|
+
def async_decorator(self) -> Callable[..., Any] | None:
|
|
81
|
+
"""
|
|
82
|
+
Decorator to apply the async_impl decorator to the wrapped function
|
|
83
|
+
|
|
84
|
+
Default implementation is a no-op
|
|
85
|
+
"""
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
def sync_ast_transform(
|
|
89
|
+
self, wrapped_node: ast.AST, transformers_globals: dict[str, Any]
|
|
90
|
+
) -> ast.AST | None:
|
|
91
|
+
"""
|
|
92
|
+
When this decorator is encountered in the ast during sync generation, this method is called
|
|
93
|
+
to transform the wrapped node.
|
|
94
|
+
|
|
95
|
+
If None is returned, the node will be dropped from the output file.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
wrapped_node: ast node representing the wrapped function or class that is being wrapped
|
|
99
|
+
transformers_globals: the set of globals() from the transformers module. This is used to access
|
|
100
|
+
ast transformer classes that live outside the main codebase
|
|
101
|
+
Returns:
|
|
102
|
+
transformed ast node, or None if the node should be dropped
|
|
103
|
+
"""
|
|
104
|
+
return wrapped_node
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def get_for_node(cls, node: ast.Call | ast.Attribute | ast.Name) -> "AstDecorator":
|
|
108
|
+
"""
|
|
109
|
+
Build an AstDecorator instance from an ast decorator node
|
|
110
|
+
|
|
111
|
+
The right subclass is found by comparing the string representation of the
|
|
112
|
+
decorator name to the class name. (Both names are converted to lowercase and
|
|
113
|
+
underscores are removed for comparison). If a matching subclass is found,
|
|
114
|
+
a new instance is created with the provided arguments.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
node: ast.Call node representing the decorator
|
|
118
|
+
Returns:
|
|
119
|
+
AstDecorator instance corresponding to the decorator
|
|
120
|
+
Raises:
|
|
121
|
+
ValueError: if the decorator cannot be parsed
|
|
122
|
+
"""
|
|
123
|
+
import ast
|
|
124
|
+
|
|
125
|
+
# expect decorators in format @CrossSync.<decorator_name>
|
|
126
|
+
# (i.e. should be an ast.Call or an ast.Attribute)
|
|
127
|
+
root_attr = node.func if isinstance(node, ast.Call) else node
|
|
128
|
+
if not isinstance(root_attr, ast.Attribute):
|
|
129
|
+
raise ValueError("Unexpected decorator format")
|
|
130
|
+
# extract the module and decorator names
|
|
131
|
+
if cls._is_cross_sync_node(root_attr):
|
|
132
|
+
decorator_name = root_attr.attr
|
|
133
|
+
got_kwargs: dict[str, Any] = (
|
|
134
|
+
{str(kw.arg): cls._convert_ast_to_py(kw.value) for kw in node.keywords}
|
|
135
|
+
if hasattr(node, "keywords")
|
|
136
|
+
else {}
|
|
137
|
+
)
|
|
138
|
+
got_args = (
|
|
139
|
+
[cls._convert_ast_to_py(arg) for arg in node.args]
|
|
140
|
+
if hasattr(node, "args")
|
|
141
|
+
else []
|
|
142
|
+
)
|
|
143
|
+
# convert to standardized representation
|
|
144
|
+
formatted_name = decorator_name.replace("_", "").lower()
|
|
145
|
+
for subclass in cls.get_subclasses():
|
|
146
|
+
if subclass.__name__.lower() == formatted_name:
|
|
147
|
+
return subclass(*got_args, **got_kwargs)
|
|
148
|
+
raise ValueError(f"Unknown decorator encountered: {decorator_name}")
|
|
149
|
+
else:
|
|
150
|
+
raise ValueError("Not a CrossSync decorator")
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def get_subclasses(cls) -> Iterable[type["AstDecorator"]]:
|
|
154
|
+
"""
|
|
155
|
+
Get all subclasses of AstDecorator
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
list of all subclasses of AstDecorator
|
|
159
|
+
"""
|
|
160
|
+
for subclass in cls.__subclasses__():
|
|
161
|
+
yield from subclass.get_subclasses()
|
|
162
|
+
yield subclass
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def _convert_ast_to_py(cls, ast_node: ast.expr | None) -> Any:
|
|
166
|
+
"""
|
|
167
|
+
Helper to convert ast primitives to python primitives. Used when unwrapping arguments
|
|
168
|
+
"""
|
|
169
|
+
import ast
|
|
170
|
+
|
|
171
|
+
if ast_node is None:
|
|
172
|
+
return None
|
|
173
|
+
if isinstance(ast_node, ast.Constant):
|
|
174
|
+
return ast_node.value
|
|
175
|
+
if isinstance(ast_node, ast.List):
|
|
176
|
+
return [cls._convert_ast_to_py(node) for node in ast_node.elts]
|
|
177
|
+
if isinstance(ast_node, ast.Tuple):
|
|
178
|
+
return tuple(cls._convert_ast_to_py(node) for node in ast_node.elts)
|
|
179
|
+
if isinstance(ast_node, ast.Dict):
|
|
180
|
+
return {
|
|
181
|
+
cls._convert_ast_to_py(k): cls._convert_ast_to_py(v)
|
|
182
|
+
for k, v in zip(ast_node.keys, ast_node.values)
|
|
183
|
+
}
|
|
184
|
+
# unsupported node type
|
|
185
|
+
return ast_node
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def _is_cross_sync_node(node: ast.AST) -> bool:
|
|
189
|
+
"""
|
|
190
|
+
Check if an AST node refers to a CrossSync attribute.
|
|
191
|
+
"""
|
|
192
|
+
import ast
|
|
193
|
+
|
|
194
|
+
if isinstance(node, ast.Attribute):
|
|
195
|
+
if isinstance(node.value, ast.Name) and node.value.id == "CrossSync":
|
|
196
|
+
return True
|
|
197
|
+
return AstDecorator._is_cross_sync_node(node.value)
|
|
198
|
+
if isinstance(node, ast.Call):
|
|
199
|
+
return AstDecorator._is_cross_sync_node(node.func)
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class ConvertClass(AstDecorator):
|
|
204
|
+
"""
|
|
205
|
+
Class decorator for guiding generation of sync classes
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
sync_name: use a new name for the sync class
|
|
209
|
+
replace_symbols: a dict of symbols and replacements to use when generating sync class
|
|
210
|
+
docstring_format_vars: a dict of variables to replace in the docstring
|
|
211
|
+
rm_aio: if True, automatically strip all asyncio keywords from method. If false,
|
|
212
|
+
only keywords wrapped in CrossSync.rm_aio() calls to be removed.
|
|
213
|
+
add_mapping_for_name: when given, will add a new attribute to CrossSync,
|
|
214
|
+
so the original class and its sync version can be accessed from CrossSync.<name>
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
def __init__(
|
|
218
|
+
self,
|
|
219
|
+
sync_name: str | None = None,
|
|
220
|
+
*,
|
|
221
|
+
replace_symbols: dict[str, str] | None = None,
|
|
222
|
+
docstring_format_vars: dict[str, tuple[str | None, str | None]] | None = None,
|
|
223
|
+
rm_aio: bool = False,
|
|
224
|
+
add_mapping_for_name: str | None = None,
|
|
225
|
+
):
|
|
226
|
+
self.sync_name = sync_name
|
|
227
|
+
self.replace_symbols = replace_symbols
|
|
228
|
+
docstring_format_vars = docstring_format_vars or {}
|
|
229
|
+
self.async_docstring_format_vars = {
|
|
230
|
+
k: v[0] or "" for k, v in docstring_format_vars.items()
|
|
231
|
+
}
|
|
232
|
+
self.sync_docstring_format_vars = {
|
|
233
|
+
k: v[1] or "" for k, v in docstring_format_vars.items()
|
|
234
|
+
}
|
|
235
|
+
self.rm_aio = rm_aio
|
|
236
|
+
self.add_mapping_for_name = add_mapping_for_name
|
|
237
|
+
|
|
238
|
+
def async_decorator(self):
|
|
239
|
+
"""
|
|
240
|
+
Use async decorator as a hook to update CrossSync mappings
|
|
241
|
+
"""
|
|
242
|
+
from .cross_sync import CrossSync
|
|
243
|
+
|
|
244
|
+
if not self.add_mapping_for_name and not self.async_docstring_format_vars:
|
|
245
|
+
# return None if no changes needed
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
new_mapping = self.add_mapping_for_name
|
|
249
|
+
|
|
250
|
+
def decorator(cls):
|
|
251
|
+
if new_mapping:
|
|
252
|
+
CrossSync.add_mapping(new_mapping, cls)
|
|
253
|
+
if self.async_docstring_format_vars:
|
|
254
|
+
cls.__doc__ = cls.__doc__.format(**self.async_docstring_format_vars)
|
|
255
|
+
return cls
|
|
256
|
+
|
|
257
|
+
return decorator
|
|
258
|
+
|
|
259
|
+
def sync_ast_transform(self, wrapped_node, transformers_globals):
|
|
260
|
+
"""
|
|
261
|
+
Transform async class into sync copy
|
|
262
|
+
"""
|
|
263
|
+
import ast
|
|
264
|
+
import copy
|
|
265
|
+
|
|
266
|
+
# copy wrapped node
|
|
267
|
+
wrapped_node = copy.deepcopy(wrapped_node)
|
|
268
|
+
# update name
|
|
269
|
+
if self.sync_name:
|
|
270
|
+
wrapped_node.name = self.sync_name
|
|
271
|
+
# strip CrossSync decorators
|
|
272
|
+
if hasattr(wrapped_node, "decorator_list"):
|
|
273
|
+
wrapped_node.decorator_list = [
|
|
274
|
+
d
|
|
275
|
+
for d in wrapped_node.decorator_list
|
|
276
|
+
if not self._is_cross_sync_node(d)
|
|
277
|
+
]
|
|
278
|
+
else:
|
|
279
|
+
wrapped_node.decorator_list = []
|
|
280
|
+
# strip async keywords if specified
|
|
281
|
+
if self.rm_aio:
|
|
282
|
+
wrapped_node = transformers_globals["AsyncToSync"]().visit(wrapped_node)
|
|
283
|
+
# add mapping decorator if needed
|
|
284
|
+
if self.add_mapping_for_name:
|
|
285
|
+
wrapped_node.decorator_list.append(
|
|
286
|
+
ast.Call(
|
|
287
|
+
func=ast.Attribute(
|
|
288
|
+
value=ast.Name(id="CrossSync", ctx=ast.Load()),
|
|
289
|
+
attr="add_mapping_decorator",
|
|
290
|
+
ctx=ast.Load(),
|
|
291
|
+
),
|
|
292
|
+
args=[
|
|
293
|
+
ast.Constant(value=self.add_mapping_for_name),
|
|
294
|
+
],
|
|
295
|
+
keywords=[],
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
# replace symbols if specified
|
|
299
|
+
if self.replace_symbols:
|
|
300
|
+
wrapped_node = transformers_globals["SymbolReplacer"](
|
|
301
|
+
self.replace_symbols
|
|
302
|
+
).visit(wrapped_node)
|
|
303
|
+
# update docstring if specified
|
|
304
|
+
if self.sync_docstring_format_vars:
|
|
305
|
+
docstring = ast.get_docstring(wrapped_node)
|
|
306
|
+
if docstring:
|
|
307
|
+
wrapped_node.body[0].value = ast.Constant(
|
|
308
|
+
value=docstring.format(**self.sync_docstring_format_vars)
|
|
309
|
+
)
|
|
310
|
+
return wrapped_node
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class Convert(ConvertClass):
|
|
314
|
+
"""
|
|
315
|
+
Method decorator to mark async methods to be converted to sync methods
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
sync_name: use a new name for the sync method
|
|
319
|
+
replace_symbols: a dict of symbols and replacements to use when generating sync method
|
|
320
|
+
docstring_format_vars: a dict of variables to replace in the docstring
|
|
321
|
+
rm_aio: if True, automatically strip all asyncio keywords from method. If False,
|
|
322
|
+
only the signature `async def` is stripped. Other keywords must be wrapped in
|
|
323
|
+
CrossSync.rm_aio() calls to be removed.
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
def __init__(
|
|
327
|
+
self,
|
|
328
|
+
sync_name: str | None = None,
|
|
329
|
+
*,
|
|
330
|
+
replace_symbols: dict[str, str] | None = None,
|
|
331
|
+
docstring_format_vars: dict[str, tuple[str | None, str | None]] | None = None,
|
|
332
|
+
rm_aio: bool = True,
|
|
333
|
+
):
|
|
334
|
+
super().__init__(
|
|
335
|
+
sync_name=sync_name,
|
|
336
|
+
replace_symbols=replace_symbols,
|
|
337
|
+
docstring_format_vars=docstring_format_vars,
|
|
338
|
+
rm_aio=rm_aio,
|
|
339
|
+
add_mapping_for_name=None,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
def sync_ast_transform(self, wrapped_node, transformers_globals):
|
|
343
|
+
"""
|
|
344
|
+
Transform async method into sync
|
|
345
|
+
"""
|
|
346
|
+
import ast
|
|
347
|
+
|
|
348
|
+
# replace async function with sync function
|
|
349
|
+
converted = ast.copy_location(
|
|
350
|
+
ast.FunctionDef(
|
|
351
|
+
wrapped_node.name,
|
|
352
|
+
wrapped_node.args,
|
|
353
|
+
wrapped_node.body,
|
|
354
|
+
wrapped_node.decorator_list
|
|
355
|
+
if hasattr(wrapped_node, "decorator_list")
|
|
356
|
+
else [],
|
|
357
|
+
wrapped_node.returns if hasattr(wrapped_node, "returns") else None,
|
|
358
|
+
),
|
|
359
|
+
wrapped_node,
|
|
360
|
+
)
|
|
361
|
+
# transform based on arguments
|
|
362
|
+
return super().sync_ast_transform(converted, transformers_globals)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class Drop(AstDecorator):
|
|
366
|
+
"""
|
|
367
|
+
Method decorator to drop methods or classes from the sync output
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
def sync_ast_transform(self, wrapped_node, transformers_globals):
|
|
371
|
+
"""
|
|
372
|
+
Drop from sync output
|
|
373
|
+
"""
|
|
374
|
+
return None
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class Pytest(AstDecorator):
|
|
378
|
+
"""
|
|
379
|
+
Used in place of pytest.mark.asyncio to mark tests
|
|
380
|
+
|
|
381
|
+
When generating sync version, also runs rm_aio to remove async keywords from
|
|
382
|
+
entire test function
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
rm_aio: if True, automatically strip all asyncio keywords from test code.
|
|
386
|
+
Defaults to True, to simplify test code generation.
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
def __init__(self, rm_aio=True):
|
|
390
|
+
self.rm_aio = rm_aio
|
|
391
|
+
|
|
392
|
+
def async_decorator(self):
|
|
393
|
+
import pytest
|
|
394
|
+
|
|
395
|
+
return pytest.mark.asyncio
|
|
396
|
+
|
|
397
|
+
def sync_ast_transform(self, wrapped_node, transformers_globals):
|
|
398
|
+
"""
|
|
399
|
+
convert async to sync
|
|
400
|
+
"""
|
|
401
|
+
import ast
|
|
402
|
+
|
|
403
|
+
# always convert method to sync
|
|
404
|
+
converted = ast.copy_location(
|
|
405
|
+
ast.FunctionDef(
|
|
406
|
+
wrapped_node.name,
|
|
407
|
+
wrapped_node.args,
|
|
408
|
+
wrapped_node.body,
|
|
409
|
+
wrapped_node.decorator_list
|
|
410
|
+
if hasattr(wrapped_node, "decorator_list")
|
|
411
|
+
else [],
|
|
412
|
+
wrapped_node.returns if hasattr(wrapped_node, "returns") else None,
|
|
413
|
+
),
|
|
414
|
+
wrapped_node,
|
|
415
|
+
)
|
|
416
|
+
# convert entire body to sync if rm_aio is set
|
|
417
|
+
if self.rm_aio:
|
|
418
|
+
converted = transformers_globals["AsyncToSync"]().visit(converted)
|
|
419
|
+
return converted
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class PytestFixture(AstDecorator):
|
|
423
|
+
"""
|
|
424
|
+
Used in place of pytest.fixture or pytest.mark.asyncio to mark fixtures
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
*args: all arguments to pass to pytest.fixture
|
|
428
|
+
**kwargs: all keyword arguments to pass to pytest.fixture
|
|
429
|
+
"""
|
|
430
|
+
|
|
431
|
+
def __init__(self, *args, **kwargs):
|
|
432
|
+
self._args = args
|
|
433
|
+
self._kwargs = kwargs
|
|
434
|
+
|
|
435
|
+
def async_decorator(self):
|
|
436
|
+
import pytest_asyncio # type: ignore
|
|
437
|
+
|
|
438
|
+
return lambda f: pytest_asyncio.fixture(*self._args, **self._kwargs)(f)
|
|
439
|
+
|
|
440
|
+
def sync_ast_transform(self, wrapped_node, transformers_globals):
|
|
441
|
+
import ast
|
|
442
|
+
import copy
|
|
443
|
+
|
|
444
|
+
arg_nodes = [
|
|
445
|
+
a if isinstance(a, ast.expr) else ast.Constant(value=a) for a in self._args
|
|
446
|
+
]
|
|
447
|
+
kwarg_nodes = []
|
|
448
|
+
for k, v in self._kwargs.items():
|
|
449
|
+
if not isinstance(v, ast.expr):
|
|
450
|
+
v = ast.Constant(value=v)
|
|
451
|
+
kwarg_nodes.append(ast.keyword(arg=k, value=v))
|
|
452
|
+
|
|
453
|
+
new_node = copy.deepcopy(wrapped_node)
|
|
454
|
+
if not hasattr(new_node, "decorator_list"):
|
|
455
|
+
new_node.decorator_list = []
|
|
456
|
+
new_node.decorator_list.append(
|
|
457
|
+
ast.Call(
|
|
458
|
+
func=ast.Attribute(
|
|
459
|
+
value=ast.Name(id="pytest", ctx=ast.Load()),
|
|
460
|
+
attr="fixture",
|
|
461
|
+
ctx=ast.Load(),
|
|
462
|
+
),
|
|
463
|
+
args=arg_nodes,
|
|
464
|
+
keywords=kwarg_nodes,
|
|
465
|
+
)
|
|
466
|
+
)
|
|
467
|
+
return new_node
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MappingMeta(type):
|
|
20
|
+
"""
|
|
21
|
+
Metaclass to provide add_mapping functionality, allowing users to add
|
|
22
|
+
custom attributes to derived classes at runtime.
|
|
23
|
+
|
|
24
|
+
Using a metaclass allows us to share functionality between CrossSync
|
|
25
|
+
and CrossSync._Sync_Impl, and it works better with mypy checks than
|
|
26
|
+
monkypatching
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# list of attributes that can be added to the derived class at runtime
|
|
30
|
+
_runtime_replacements: dict[tuple[MappingMeta, str], Any] = {}
|
|
31
|
+
|
|
32
|
+
def add_mapping(cls: MappingMeta, name: str, value: Any):
|
|
33
|
+
"""
|
|
34
|
+
Add a new attribute to the class, for replacing library-level symbols
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
- AttributeError if the attribute already exists with a different value
|
|
38
|
+
"""
|
|
39
|
+
key = (cls, name)
|
|
40
|
+
old_value = cls._runtime_replacements.get(key)
|
|
41
|
+
if old_value is None:
|
|
42
|
+
cls._runtime_replacements[key] = value
|
|
43
|
+
elif old_value != value:
|
|
44
|
+
raise AttributeError(f"Conflicting assignments for CrossSync.{name}")
|
|
45
|
+
|
|
46
|
+
def add_mapping_decorator(cls: MappingMeta, name: str):
|
|
47
|
+
"""
|
|
48
|
+
Exposes add_mapping as a class decorator
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def decorator(wrapped_cls):
|
|
52
|
+
cls.add_mapping(name, wrapped_cls)
|
|
53
|
+
return wrapped_cls
|
|
54
|
+
|
|
55
|
+
return decorator
|
|
56
|
+
|
|
57
|
+
def __getattr__(cls: MappingMeta, name: str):
|
|
58
|
+
"""
|
|
59
|
+
Retrieve custom attributes
|
|
60
|
+
"""
|
|
61
|
+
key = (cls, name)
|
|
62
|
+
found = cls._runtime_replacements.get(key)
|
|
63
|
+
if found is not None:
|
|
64
|
+
return found
|
|
65
|
+
raise AttributeError(f"CrossSync has no attribute {name}")
|