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.
Files changed (260) hide show
  1. {google_cloud_spanner-3.63.0/google_cloud_spanner.egg-info → google_cloud_spanner-3.64.0}/PKG-INFO +4 -3
  2. google_cloud_spanner-3.64.0/google/cloud/aio/_cross_sync/__init__.py +19 -0
  3. google_cloud_spanner-3.64.0/google/cloud/aio/_cross_sync/_decorators.py +467 -0
  4. google_cloud_spanner-3.64.0/google/cloud/aio/_cross_sync/_mapping_meta.py +65 -0
  5. google_cloud_spanner-3.64.0/google/cloud/aio/_cross_sync/cross_sync.py +424 -0
  6. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner.py +13 -12
  7. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/__init__.py +74 -68
  8. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/gapic_version.py +1 -1
  9. {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
  10. {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
  11. {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
  12. {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
  13. {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
  14. {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
  15. {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
  16. {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
  17. {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
  18. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/__init__.py +2 -7
  19. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/backup.py +18 -16
  20. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/backup_schedule.py +3 -4
  21. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/types/common.py +4 -4
  22. {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
  23. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/__init__.py +46 -48
  24. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/gapic_version.py +1 -1
  25. {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
  26. {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
  27. {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
  28. {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
  29. {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
  30. {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
  31. {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
  32. {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
  33. {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
  34. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/types/__init__.py +1 -5
  35. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/types/common.py +2 -3
  36. {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
  37. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/__init__.py +28 -30
  38. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/_helpers.py +0 -1
  39. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/batch_dml_executor.py +19 -4
  40. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/connection.py +51 -14
  41. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/cursor.py +57 -31
  42. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/parse_utils.py +3 -2
  43. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/partition_helper.py +3 -4
  44. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/transaction_helper.py +7 -6
  45. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/types.py +3 -3
  46. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/version.py +1 -1
  47. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/__init__.py +66 -53
  48. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/_helpers.py +131 -0
  49. {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
  50. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/client.py +708 -0
  51. {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
  52. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/database_sessions_manager.py +243 -0
  53. {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
  54. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/pool.py +974 -0
  55. {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
  56. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/snapshot.py +857 -0
  57. {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
  58. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/testing/database_test.py +207 -0
  59. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/testing/interceptors.py +107 -0
  60. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/_async/transaction.py +881 -0
  61. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/_helpers.py +190 -27
  62. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/_opentelemetry_tracing.py +9 -10
  63. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/backup.py +7 -6
  64. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/batch.py +419 -0
  65. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/client.py +95 -92
  66. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/data_types.py +3 -2
  67. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/database.py +1869 -0
  68. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/database_sessions_manager.py +53 -109
  69. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/gapic_version.py +1 -1
  70. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/instance.py +692 -0
  71. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/keyset.py +3 -5
  72. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/merged_result_set.py +9 -6
  73. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_capture.py +17 -0
  74. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_exporter.py +59 -34
  75. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_interceptor.py +4 -6
  76. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_tracer.py +5 -3
  77. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/metrics_tracer_factory.py +17 -18
  78. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/spanner_metrics_tracer_factory.py +9 -6
  79. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/param_types.py +7 -5
  80. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/pool.py +192 -194
  81. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/async_client.py +27 -22
  82. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/client.py +34 -27
  83. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/pagers.py +8 -7
  84. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/__init__.py +1 -3
  85. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/base.py +23 -13
  86. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/grpc.py +19 -14
  87. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/grpc_asyncio.py +20 -15
  88. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/rest.py +24 -22
  89. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/transports/rest_base.py +11 -11
  90. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/session.py +608 -0
  91. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/snapshot.py +221 -401
  92. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/snapshot_helpers.py +730 -0
  93. google_cloud_spanner-3.64.0/google/cloud/spanner_v1/streamed.py +367 -0
  94. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/table.py +1 -5
  95. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/database_test.py +30 -19
  96. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/interceptors.py +3 -2
  97. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/mock_database_admin.py +1 -0
  98. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/mock_spanner.py +60 -7
  99. {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
  100. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/testing/spanner_pb2_grpc.py +1 -0
  101. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/transaction.py +94 -187
  102. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/__init__.py +7 -30
  103. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/change_stream.py +16 -15
  104. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/commit_response.py +15 -3
  105. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/keys.py +1 -3
  106. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/location.py +10 -11
  107. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/mutation.py +2 -3
  108. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/query_plan.py +2 -3
  109. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/result_set.py +14 -2
  110. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/spanner.py +105 -38
  111. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/transaction.py +44 -25
  112. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/types/type.py +2 -1
  113. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0/google_cloud_spanner.egg-info}/PKG-INFO +4 -3
  114. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/SOURCES.txt +42 -0
  115. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/setup.py +3 -2
  116. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/_builders.py +59 -13
  117. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/_fixtures.py +0 -1
  118. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/_helpers.py +8 -4
  119. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/mock_server_test_base.py +203 -37
  120. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_aborted_transaction.py +7 -6
  121. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_basics.py +5 -9
  122. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_dbapi_autocommit.py +4 -5
  123. google_cloud_spanner-3.64.0/tests/mockserver_tests/test_dbapi_inline_begin.py +906 -0
  124. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_dbapi_isolation_level.py +19 -44
  125. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_request_id_header.py +8 -7
  126. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/mockserver_tests/test_tags.py +10 -13
  127. google_cloud_spanner-3.64.0/tests/system/_async/__init__.py +0 -0
  128. google_cloud_spanner-3.64.0/tests/system/_async/conftest.py +207 -0
  129. google_cloud_spanner-3.64.0/tests/system/_async/pytest.ini +4 -0
  130. google_cloud_spanner-3.64.0/tests/system/_async/test_database_api.py +194 -0
  131. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/_helpers.py +11 -5
  132. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/_sample_data.py +79 -0
  133. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/conftest.py +6 -2
  134. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_backup_api.py +3 -2
  135. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_database_api.py +6 -9
  136. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_dbapi.py +153 -94
  137. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_instance_api.py +0 -1
  138. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_session_api.py +26 -22
  139. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_table_api.py +2 -1
  140. {google_cloud_spanner-3.63.0/tests/unit → google_cloud_spanner-3.64.0/tests/system}/testdata/singer_pb2.py +1 -0
  141. google_cloud_spanner-3.64.0/tests/system/utils/__init__.py +0 -0
  142. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/utils/clear_streaming.py +3 -5
  143. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/utils/populate_streaming.py +12 -13
  144. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/utils/scrub_instances.py +1 -0
  145. google_cloud_spanner-3.64.0/tests/unit/_async/test_batch.py +301 -0
  146. google_cloud_spanner-3.64.0/tests/unit/_async/test_client.py +780 -0
  147. google_cloud_spanner-3.64.0/tests/unit/_async/test_client_extra.py +225 -0
  148. google_cloud_spanner-3.64.0/tests/unit/_async/test_database.py +4361 -0
  149. google_cloud_spanner-3.64.0/tests/unit/_async/test_database_extra.py +513 -0
  150. google_cloud_spanner-3.64.0/tests/unit/_async/test_helpers_extra.py +143 -0
  151. google_cloud_spanner-3.64.0/tests/unit/_async/test_instance_extra.py +207 -0
  152. google_cloud_spanner-3.64.0/tests/unit/_async/test_pool.py +954 -0
  153. google_cloud_spanner-3.64.0/tests/unit/_async/test_session.py +2822 -0
  154. google_cloud_spanner-3.64.0/tests/unit/_async/test_sessions_manager_extra.py +218 -0
  155. google_cloud_spanner-3.64.0/tests/unit/_async/test_snapshot.py +1142 -0
  156. google_cloud_spanner-3.64.0/tests/unit/_async/test_streamed.py +1336 -0
  157. google_cloud_spanner-3.64.0/tests/unit/_async/test_streamed_extra.py +114 -0
  158. google_cloud_spanner-3.64.0/tests/unit/_async/test_transaction.py +1596 -0
  159. google_cloud_spanner-3.64.0/tests/unit/_async/test_transaction_extra.py +235 -0
  160. google_cloud_spanner-3.64.0/tests/unit/conftest.py +18 -0
  161. google_cloud_spanner-3.64.0/tests/unit/gapic/conftest.py +20 -0
  162. {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
  163. {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
  164. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_v1/test_spanner.py +324 -280
  165. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_checksum.py +16 -8
  166. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_connect.py +13 -9
  167. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_connection.py +32 -10
  168. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_cursor.py +16 -11
  169. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_globals.py +1 -3
  170. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_parse_utils.py +4 -5
  171. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_parser.py +28 -29
  172. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_transaction_helper.py +5 -7
  173. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_types.py +0 -1
  174. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test__helpers.py +104 -94
  175. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test__opentelemetry_tracing.py +7 -10
  176. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_atomic_counter.py +1 -0
  177. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_backup.py +24 -19
  178. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_batch.py +39 -27
  179. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_client.py +41 -29
  180. google_cloud_spanner-3.64.0/tests/unit/test_client_context.py +441 -0
  181. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_database.py +153 -86
  182. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_database_session_manager.py +62 -16
  183. google_cloud_spanner-3.64.0/tests/unit/test_decorators_extra.py +350 -0
  184. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_exceptions.py +1 -0
  185. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_instance.py +72 -49
  186. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_keyset.py +1 -2
  187. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_merged_result_set.py +3 -4
  188. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics.py +34 -12
  189. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_capture.py +3 -1
  190. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_concurrency.py +2 -1
  191. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_exporter.py +18 -21
  192. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_interceptor.py +3 -1
  193. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_tracer.py +4 -3
  194. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_metrics_tracer_factory.py +3 -9
  195. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_param_types.py +10 -10
  196. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_pool.py +739 -293
  197. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_session.py +59 -46
  198. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_snapshot.py +58 -52
  199. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_spanner.py +42 -25
  200. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_spanner_metrics_tracer_factory.py +1 -0
  201. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_streamed.py +16 -23
  202. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_table.py +2 -6
  203. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_transaction.py +34 -26
  204. {google_cloud_spanner-3.63.0/tests/system → google_cloud_spanner-3.64.0/tests/unit}/testdata/singer_pb2.py +1 -0
  205. google_cloud_spanner-3.63.0/tests/unit/conftest.py +0 -27
  206. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/LICENSE +0 -0
  207. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/MANIFEST.in +0 -0
  208. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/README.rst +0 -0
  209. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/gapic_metadata.json +0 -0
  210. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/py.typed +0 -0
  211. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_database_v1/services/__init__.py +0 -0
  212. {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
  213. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/gapic_metadata.json +0 -0
  214. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/py.typed +0 -0
  215. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_admin_instance_v1/services/__init__.py +0 -0
  216. {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
  217. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/checksum.py +0 -0
  218. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/client_side_statement_executor.py +6 -6
  219. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/client_side_statement_parser.py +2 -2
  220. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/exceptions.py +0 -0
  221. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/parsed_statement.py +0 -0
  222. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/parser.py +0 -0
  223. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_dbapi/utils.py +0 -0
  224. {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
  225. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/exceptions.py +0 -0
  226. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/gapic_metadata.json +0 -0
  227. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/metrics/constants.py +0 -0
  228. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/py.typed +0 -0
  229. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/request_id_header.py +0 -0
  230. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/__init__.py +0 -0
  231. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google/cloud/spanner_v1/services/spanner/__init__.py +1 -1
  232. {google_cloud_spanner-3.63.0/tests/mockserver_tests → google_cloud_spanner-3.64.0/google/cloud/spanner_v1/testing}/__init__.py +0 -0
  233. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/dependency_links.txt +0 -0
  234. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/not-zip-safe +0 -0
  235. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/requires.txt +0 -0
  236. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/google_cloud_spanner.egg-info/top_level.txt +0 -0
  237. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/setup.cfg +0 -0
  238. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/__init__.py +0 -0
  239. {google_cloud_spanner-3.63.0/tests/system → google_cloud_spanner-3.64.0/tests/mockserver_tests}/__init__.py +0 -0
  240. {google_cloud_spanner-3.63.0/tests/system/utils → google_cloud_spanner-3.64.0/tests/system}/__init__.py +0 -0
  241. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_metrics.py +1 -1
  242. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_observability_options.py +10 -10
  243. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/test_streaming_chunking.py +0 -0
  244. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/testdata/descriptors.pb +0 -0
  245. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/testdata/singer.proto +0 -0
  246. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/system/utils/streaming_utils.py +0 -0
  247. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/__init__.py +0 -0
  248. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/__init__.py +0 -0
  249. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_admin_database_v1/__init__.py +0 -0
  250. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_admin_instance_v1/__init__.py +0 -0
  251. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/gapic/spanner_v1/__init__.py +0 -0
  252. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/__init__.py +0 -0
  253. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test__helpers.py +0 -0
  254. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_batch_dml_executor.py +0 -0
  255. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_client_side_statement_executor.py +0 -0
  256. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/spanner_dbapi/test_utils.py +0 -0
  257. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/streaming-read-acceptance-test.json +0 -0
  258. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_datatypes.py +1 -1
  259. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/test_packaging.py +0 -0
  260. {google_cloud_spanner-3.63.0 → google_cloud_spanner-3.64.0}/tests/unit/testdata/singer.proto +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-cloud-spanner
3
- Version: 3.63.0
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.9
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}")