google-cloud-spanner 3.50.1__tar.gz → 3.52.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 (196) hide show
  1. {google_cloud_spanner-3.50.1/google_cloud_spanner.egg-info → google_cloud_spanner-3.52.0}/PKG-INFO +2 -1
  2. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/gapic_version.py +1 -1
  3. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/gapic_version.py +1 -1
  4. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/connection.py +53 -12
  5. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/cursor.py +39 -3
  6. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/parse_utils.py +9 -2
  7. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/transaction_helper.py +1 -1
  8. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/_helpers.py +254 -47
  9. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/_opentelemetry_tracing.py +144 -0
  10. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/batch.py +37 -5
  11. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/client.py +21 -0
  12. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/database.py +187 -107
  13. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/gapic_version.py +1 -1
  14. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/merged_result_set.py +12 -0
  15. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/metrics/constants.py +63 -0
  16. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/metrics/metrics_exporter.py +392 -0
  17. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/metrics/metrics_tracer.py +558 -0
  18. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/metrics/metrics_tracer_factory.py +309 -0
  19. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/pool.py +197 -26
  20. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/request_id_header.py +42 -0
  21. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/session.py +152 -100
  22. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/snapshot.py +114 -19
  23. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/streamed.py +55 -10
  24. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/testing/mock_database_admin.py +38 -0
  25. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/testing/mock_spanner.py +264 -0
  26. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/testing/spanner_database_admin_pb2_grpc.py +1267 -0
  27. google_cloud_spanner-3.52.0/google/cloud/spanner_v1/testing/spanner_pb2_grpc.py +882 -0
  28. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/transaction.py +95 -37
  29. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0/google_cloud_spanner.egg-info}/PKG-INFO +2 -1
  30. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google_cloud_spanner.egg-info/SOURCES.txt +20 -0
  31. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google_cloud_spanner.egg-info/requires.txt +1 -0
  32. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/setup.py +1 -0
  33. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/_helpers.py +61 -4
  34. google_cloud_spanner-3.52.0/tests/mockserver_tests/mock_server_test_base.py +191 -0
  35. google_cloud_spanner-3.52.0/tests/mockserver_tests/test_aborted_transaction.py +143 -0
  36. google_cloud_spanner-3.52.0/tests/mockserver_tests/test_basics.py +109 -0
  37. google_cloud_spanner-3.52.0/tests/mockserver_tests/test_tags.py +206 -0
  38. google_cloud_spanner-3.52.0/tests/system/__init__.py +0 -0
  39. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/_helpers.py +18 -0
  40. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/test_dbapi.py +45 -2
  41. google_cloud_spanner-3.52.0/tests/system/test_observability_options.py +454 -0
  42. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/test_session_api.py +99 -54
  43. google_cloud_spanner-3.52.0/tests/system/utils/__init__.py +0 -0
  44. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_connection.py +31 -0
  45. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_parse_utils.py +7 -1
  46. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test__helpers.py +60 -0
  47. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test__opentelemetry_tracing.py +73 -0
  48. google_cloud_spanner-3.52.0/tests/unit/test_atomic_counter.py +78 -0
  49. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_batch.py +54 -7
  50. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_database.py +12 -5
  51. google_cloud_spanner-3.52.0/tests/unit/test_metric_exporter.py +488 -0
  52. google_cloud_spanner-3.52.0/tests/unit/test_metrics_tracer.py +224 -0
  53. google_cloud_spanner-3.52.0/tests/unit/test_metrics_tracer_factory.py +59 -0
  54. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_pool.py +433 -187
  55. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_session.py +56 -5
  56. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_snapshot.py +40 -22
  57. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_spanner.py +4 -0
  58. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_transaction.py +99 -10
  59. google_cloud_spanner-3.50.1/google/cloud/spanner_v1/_opentelemetry_tracing.py +0 -85
  60. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/LICENSE +0 -0
  61. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/MANIFEST.in +0 -0
  62. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/README.rst +0 -0
  63. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner.py +0 -0
  64. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/__init__.py +0 -0
  65. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/gapic_metadata.json +0 -0
  66. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/py.typed +0 -0
  67. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/__init__.py +0 -0
  68. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/__init__.py +0 -0
  69. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py +0 -0
  70. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/client.py +0 -0
  71. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/pagers.py +0 -0
  72. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/__init__.py +0 -0
  73. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py +0 -0
  74. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py +0 -0
  75. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py +0 -0
  76. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py +0 -0
  77. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py +0 -0
  78. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/types/__init__.py +0 -0
  79. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/types/backup.py +0 -0
  80. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/types/backup_schedule.py +0 -0
  81. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/types/common.py +0 -0
  82. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py +0 -0
  83. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/__init__.py +0 -0
  84. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/gapic_metadata.json +0 -0
  85. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/py.typed +0 -0
  86. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/__init__.py +0 -0
  87. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/__init__.py +0 -0
  88. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py +0 -0
  89. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py +0 -0
  90. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/pagers.py +0 -0
  91. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/__init__.py +0 -0
  92. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py +0 -0
  93. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py +0 -0
  94. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py +0 -0
  95. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py +0 -0
  96. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py +0 -0
  97. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/types/__init__.py +0 -0
  98. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/types/common.py +0 -0
  99. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_admin_instance_v1/types/spanner_instance_admin.py +0 -0
  100. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/__init__.py +0 -0
  101. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/_helpers.py +0 -0
  102. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/batch_dml_executor.py +0 -0
  103. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/checksum.py +0 -0
  104. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/client_side_statement_executor.py +0 -0
  105. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/client_side_statement_parser.py +0 -0
  106. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/exceptions.py +0 -0
  107. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/parsed_statement.py +0 -0
  108. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/parser.py +0 -0
  109. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/partition_helper.py +0 -0
  110. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/types.py +0 -0
  111. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/utils.py +0 -0
  112. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_dbapi/version.py +0 -0
  113. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/__init__.py +0 -0
  114. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/backup.py +0 -0
  115. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/data_types.py +0 -0
  116. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/gapic_metadata.json +0 -0
  117. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/instance.py +0 -0
  118. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/keyset.py +0 -0
  119. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/param_types.py +0 -0
  120. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/py.typed +0 -0
  121. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/__init__.py +0 -0
  122. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/__init__.py +0 -0
  123. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/async_client.py +0 -0
  124. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/client.py +0 -0
  125. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/pagers.py +0 -0
  126. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/transports/__init__.py +0 -0
  127. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/transports/base.py +0 -0
  128. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/transports/grpc.py +0 -0
  129. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/transports/grpc_asyncio.py +0 -0
  130. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/transports/rest.py +0 -0
  131. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/services/spanner/transports/rest_base.py +0 -0
  132. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/table.py +0 -0
  133. {google_cloud_spanner-3.50.1/tests/system → google_cloud_spanner-3.52.0/google/cloud/spanner_v1/testing}/__init__.py +0 -0
  134. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/testing/database_test.py +0 -0
  135. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/testing/interceptors.py +0 -0
  136. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/__init__.py +0 -0
  137. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/commit_response.py +0 -0
  138. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/keys.py +0 -0
  139. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/mutation.py +0 -0
  140. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/query_plan.py +0 -0
  141. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/result_set.py +0 -0
  142. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/spanner.py +0 -0
  143. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/transaction.py +0 -0
  144. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google/cloud/spanner_v1/types/type.py +0 -0
  145. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google_cloud_spanner.egg-info/dependency_links.txt +0 -0
  146. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google_cloud_spanner.egg-info/not-zip-safe +0 -0
  147. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/google_cloud_spanner.egg-info/top_level.txt +0 -0
  148. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/setup.cfg +0 -0
  149. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/__init__.py +0 -0
  150. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/_fixtures.py +0 -0
  151. {google_cloud_spanner-3.50.1/tests/system/utils → google_cloud_spanner-3.52.0/tests/mockserver_tests}/__init__.py +0 -0
  152. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/_sample_data.py +0 -0
  153. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/conftest.py +0 -0
  154. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/test_backup_api.py +0 -0
  155. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/test_database_api.py +0 -0
  156. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/test_instance_api.py +0 -0
  157. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/test_streaming_chunking.py +0 -0
  158. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/test_table_api.py +0 -0
  159. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/testdata/descriptors.pb +0 -0
  160. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/testdata/singer.proto +0 -0
  161. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/testdata/singer_pb2.py +0 -0
  162. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/utils/clear_streaming.py +0 -0
  163. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/utils/populate_streaming.py +0 -0
  164. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/utils/scrub_instances.py +0 -0
  165. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/system/utils/streaming_utils.py +0 -0
  166. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/__init__.py +0 -0
  167. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/gapic/__init__.py +0 -0
  168. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/gapic/spanner_admin_database_v1/__init__.py +0 -0
  169. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/gapic/spanner_admin_database_v1/test_database_admin.py +0 -0
  170. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/gapic/spanner_admin_instance_v1/__init__.py +0 -0
  171. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/gapic/spanner_admin_instance_v1/test_instance_admin.py +0 -0
  172. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/gapic/spanner_v1/__init__.py +0 -0
  173. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/gapic/spanner_v1/test_spanner.py +0 -0
  174. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/__init__.py +0 -0
  175. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test__helpers.py +0 -0
  176. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_batch_dml_executor.py +0 -0
  177. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_checksum.py +0 -0
  178. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_connect.py +0 -0
  179. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_cursor.py +0 -0
  180. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_globals.py +0 -0
  181. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_parser.py +0 -0
  182. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_transaction_helper.py +0 -0
  183. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_types.py +0 -0
  184. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/spanner_dbapi/test_utils.py +0 -0
  185. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/streaming-read-acceptance-test.json +0 -0
  186. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_backup.py +0 -0
  187. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_client.py +0 -0
  188. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_datatypes.py +0 -0
  189. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_instance.py +0 -0
  190. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_keyset.py +0 -0
  191. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_packaging.py +0 -0
  192. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_param_types.py +0 -0
  193. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_streamed.py +0 -0
  194. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/test_table.py +0 -0
  195. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/testdata/singer.proto +0 -0
  196. {google_cloud_spanner-3.50.1 → google_cloud_spanner-3.52.0}/tests/unit/testdata/singer_pb2.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: google-cloud-spanner
3
- Version: 3.50.1
3
+ Version: 3.52.0
4
4
  Summary: Google Cloud Spanner API client library
5
5
  Home-page: https://github.com/googleapis/python-spanner
6
6
  Author: Google LLC
@@ -34,6 +34,7 @@ Provides-Extra: tracing
34
34
  Requires-Dist: opentelemetry-api>=1.22.0; extra == "tracing"
35
35
  Requires-Dist: opentelemetry-sdk>=1.22.0; extra == "tracing"
36
36
  Requires-Dist: opentelemetry-semantic-conventions>=0.43b0; extra == "tracing"
37
+ Requires-Dist: google-cloud-monitoring>=2.16.0; extra == "tracing"
37
38
  Provides-Extra: libcst
38
39
  Requires-Dist: libcst>=0.2.5; extra == "libcst"
39
40
 
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- __version__ = "3.50.1" # {x-release-please-version}
16
+ __version__ = "3.52.0" # {x-release-please-version}
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- __version__ = "3.50.1" # {x-release-please-version}
16
+ __version__ = "3.52.0" # {x-release-please-version}
@@ -89,9 +89,11 @@ class Connection:
89
89
  committed by other transactions since the start of the read-only transaction. Commit or rolling back
90
90
  the read-only transaction is semantically the same, and only indicates that the read-only transaction
91
91
  should end a that a new one should be started when the next statement is executed.
92
+
93
+ **kwargs: Initial value for connection variables.
92
94
  """
93
95
 
94
- def __init__(self, instance, database=None, read_only=False):
96
+ def __init__(self, instance, database=None, read_only=False, **kwargs):
95
97
  self._instance = instance
96
98
  self._database = database
97
99
  self._ddl_statements = []
@@ -111,12 +113,13 @@ class Connection:
111
113
  self.request_priority = None
112
114
  self._transaction_begin_marked = False
113
115
  # whether transaction started at Spanner. This means that we had
114
- # made atleast one call to Spanner.
116
+ # made at least one call to Spanner.
115
117
  self._spanner_transaction_started = False
116
118
  self._batch_mode = BatchMode.NONE
117
119
  self._batch_dml_executor: BatchDmlExecutor = None
118
120
  self._transaction_helper = TransactionRetryHelper(self)
119
121
  self._autocommit_dml_mode: AutocommitDmlMode = AutocommitDmlMode.TRANSACTIONAL
122
+ self._connection_variables = kwargs
120
123
 
121
124
  @property
122
125
  def spanner_client(self):
@@ -206,6 +209,10 @@ class Connection:
206
209
  """
207
210
  return (not self._autocommit) or self._transaction_begin_marked
208
211
 
212
+ @property
213
+ def _ignore_transaction_warnings(self):
214
+ return self._connection_variables.get("ignore_transaction_warnings", False)
215
+
209
216
  @property
210
217
  def instance(self):
211
218
  """Instance to which this connection relates.
@@ -232,7 +239,7 @@ class Connection:
232
239
  Args:
233
240
  value (bool): True for ReadOnly mode, False for ReadWrite.
234
241
  """
235
- if self._spanner_transaction_started:
242
+ if self._read_only != value and self._spanner_transaction_started:
236
243
  raise ValueError(
237
244
  "Connection read/write mode can't be changed while a transaction is in progress. "
238
245
  "Commit or rollback the current transaction and try again."
@@ -254,6 +261,28 @@ class Connection:
254
261
  self.request_priority = None
255
262
  return req_opts
256
263
 
264
+ @property
265
+ def transaction_tag(self):
266
+ """The transaction tag that will be applied to the next read/write
267
+ transaction on this `Connection`. This property is automatically cleared
268
+ when a new transaction is started.
269
+
270
+ Returns:
271
+ str: The transaction tag that will be applied to the next read/write transaction.
272
+ """
273
+ return self._connection_variables.get("transaction_tag", None)
274
+
275
+ @transaction_tag.setter
276
+ def transaction_tag(self, value):
277
+ """Sets the transaction tag for the next read/write transaction on this
278
+ `Connection`. This property is automatically cleared when a new transaction
279
+ is started.
280
+
281
+ Args:
282
+ value (str): The transaction tag for the next read/write transaction.
283
+ """
284
+ self._connection_variables["transaction_tag"] = value
285
+
257
286
  @property
258
287
  def staleness(self):
259
288
  """Current read staleness option value of this `Connection`.
@@ -270,7 +299,7 @@ class Connection:
270
299
  Args:
271
300
  value (dict): Staleness type and value.
272
301
  """
273
- if self._spanner_transaction_started:
302
+ if self._spanner_transaction_started and value != self._staleness:
274
303
  raise ValueError(
275
304
  "`staleness` option can't be changed while a transaction is in progress. "
276
305
  "Commit or rollback the current transaction and try again."
@@ -333,6 +362,8 @@ class Connection:
333
362
  if not self.read_only and self._client_transaction_started:
334
363
  if not self._spanner_transaction_started:
335
364
  self._transaction = self._session_checkout().transaction()
365
+ self._transaction.transaction_tag = self.transaction_tag
366
+ self.transaction_tag = None
336
367
  self._snapshot = None
337
368
  self._spanner_transaction_started = True
338
369
  self._transaction.begin()
@@ -398,9 +429,10 @@ class Connection:
398
429
  if self.database is None:
399
430
  raise ValueError("Database needs to be passed for this operation")
400
431
  if not self._client_transaction_started:
401
- warnings.warn(
402
- CLIENT_TRANSACTION_NOT_STARTED_WARNING, UserWarning, stacklevel=2
403
- )
432
+ if not self._ignore_transaction_warnings:
433
+ warnings.warn(
434
+ CLIENT_TRANSACTION_NOT_STARTED_WARNING, UserWarning, stacklevel=2
435
+ )
404
436
  return
405
437
 
406
438
  self.run_prior_DDL_statements()
@@ -418,9 +450,10 @@ class Connection:
418
450
  This is a no-op if there is no active client transaction.
419
451
  """
420
452
  if not self._client_transaction_started:
421
- warnings.warn(
422
- CLIENT_TRANSACTION_NOT_STARTED_WARNING, UserWarning, stacklevel=2
423
- )
453
+ if not self._ignore_transaction_warnings:
454
+ warnings.warn(
455
+ CLIENT_TRANSACTION_NOT_STARTED_WARNING, UserWarning, stacklevel=2
456
+ )
424
457
  return
425
458
  try:
426
459
  if self._spanner_transaction_started and not self._read_only:
@@ -449,7 +482,9 @@ class Connection:
449
482
 
450
483
  return self.database.update_ddl(ddl_statements).result()
451
484
 
452
- def run_statement(self, statement: Statement):
485
+ def run_statement(
486
+ self, statement: Statement, request_options: RequestOptions = None
487
+ ):
453
488
  """Run single SQL statement in begun transaction.
454
489
 
455
490
  This method is never used in autocommit mode. In
@@ -463,6 +498,9 @@ class Connection:
463
498
  :param retried: (Optional) Retry the SQL statement if statement
464
499
  execution failed. Defaults to false.
465
500
 
501
+ :type request_options: :class:`RequestOptions`
502
+ :param request_options: Request options to use for this statement.
503
+
466
504
  :rtype: :class:`google.cloud.spanner_v1.streamed.StreamedResultSet`,
467
505
  :class:`google.cloud.spanner_dbapi.checksum.ResultsChecksum`
468
506
  :returns: Streamed result set of the statement and a
@@ -473,7 +511,7 @@ class Connection:
473
511
  statement.sql,
474
512
  statement.params,
475
513
  param_types=statement.param_types,
476
- request_options=self.request_options,
514
+ request_options=request_options or self.request_options,
477
515
  )
478
516
 
479
517
  @check_not_closed
@@ -654,6 +692,7 @@ def connect(
654
692
  user_agent=None,
655
693
  client=None,
656
694
  route_to_leader_enabled=True,
695
+ **kwargs,
657
696
  ):
658
697
  """Creates a connection to a Google Cloud Spanner database.
659
698
 
@@ -696,6 +735,8 @@ def connect(
696
735
  disable leader aware routing. Disabling leader aware routing would
697
736
  route all requests in RW/PDML transactions to the closest region.
698
737
 
738
+ **kwargs: Initial value for connection variables.
739
+
699
740
 
700
741
  :rtype: :class:`google.cloud.spanner_dbapi.connection.Connection`
701
742
  :returns: Connection object associated with the given Google Cloud Spanner
@@ -50,6 +50,7 @@ from google.cloud.spanner_dbapi.parsed_statement import (
50
50
  from google.cloud.spanner_dbapi.transaction_helper import CursorStatementType
51
51
  from google.cloud.spanner_dbapi.utils import PeekIterator
52
52
  from google.cloud.spanner_dbapi.utils import StreamedManyResultSets
53
+ from google.cloud.spanner_v1 import RequestOptions
53
54
  from google.cloud.spanner_v1.merged_result_set import MergedResultSet
54
55
 
55
56
  ColumnDetails = namedtuple("column_details", ["null_ok", "spanner_type"])
@@ -97,6 +98,39 @@ class Cursor(object):
97
98
  self._parsed_statement: ParsedStatement = None
98
99
  self._in_retry_mode = False
99
100
  self._batch_dml_rows_count = None
101
+ self._request_tag = None
102
+
103
+ @property
104
+ def request_tag(self):
105
+ """The request tag that will be applied to the next statement on this
106
+ cursor. This property is automatically cleared when a statement is
107
+ executed.
108
+
109
+ Returns:
110
+ str: The request tag that will be applied to the next statement on
111
+ this cursor.
112
+ """
113
+ return self._request_tag
114
+
115
+ @request_tag.setter
116
+ def request_tag(self, value):
117
+ """Sets the request tag for the next statement on this cursor. This
118
+ property is automatically cleared when a statement is executed.
119
+
120
+ Args:
121
+ value (str): The request tag for the statement.
122
+ """
123
+ self._request_tag = value
124
+
125
+ @property
126
+ def request_options(self):
127
+ options = self.connection.request_options
128
+ if self._request_tag:
129
+ if not options:
130
+ options = RequestOptions()
131
+ options.request_tag = self._request_tag
132
+ self._request_tag = None
133
+ return options
100
134
 
101
135
  @property
102
136
  def is_closed(self):
@@ -284,7 +318,7 @@ class Cursor(object):
284
318
  sql,
285
319
  params=args,
286
320
  param_types=self._parsed_statement.statement.param_types,
287
- request_options=self.connection.request_options,
321
+ request_options=self.request_options,
288
322
  )
289
323
  self._result_set = None
290
324
  else:
@@ -318,7 +352,9 @@ class Cursor(object):
318
352
  if self.connection._client_transaction_started:
319
353
  while True:
320
354
  try:
321
- self._result_set = self.connection.run_statement(statement)
355
+ self._result_set = self.connection.run_statement(
356
+ statement, self.request_options
357
+ )
322
358
  self._itr = PeekIterator(self._result_set)
323
359
  return
324
360
  except Aborted:
@@ -478,7 +514,7 @@ class Cursor(object):
478
514
  sql,
479
515
  params,
480
516
  get_param_types(params),
481
- request_options=self.connection.request_options,
517
+ request_options=self.request_options,
482
518
  )
483
519
  # Read the first element so that the StreamedResultSet can
484
520
  # return the metadata after a DQL statement.
@@ -29,12 +29,19 @@ from .parsed_statement import ParsedStatement, StatementType, Statement
29
29
  from .types import DateStr, TimestampStr
30
30
  from .utils import sanitize_literals_for_upload
31
31
 
32
+ # Note: This mapping deliberately does not contain a value for float.
33
+ # The reason for that is that it is better to just let Spanner determine
34
+ # the parameter type instead of specifying one explicitly. The reason for
35
+ # this is that if the client specifies FLOAT64, and the actual column that
36
+ # the parameter is used for is of type FLOAT32, then Spanner will return an
37
+ # error. If however the client does not specify a type, then Spanner will
38
+ # automatically choose the appropriate type based on the column where the
39
+ # value will be inserted/updated or that it will be compared with.
32
40
  TYPES_MAP = {
33
41
  bool: spanner.param_types.BOOL,
34
42
  bytes: spanner.param_types.BYTES,
35
43
  str: spanner.param_types.STRING,
36
44
  int: spanner.param_types.INT64,
37
- float: spanner.param_types.FLOAT64,
38
45
  datetime.datetime: spanner.param_types.TIMESTAMP,
39
46
  datetime.date: spanner.param_types.DATE,
40
47
  DateStr: spanner.param_types.DATE,
@@ -148,7 +155,7 @@ STMT_UPDATING = "UPDATING"
148
155
  STMT_INSERT = "INSERT"
149
156
 
150
157
  # Heuristic for identifying statements that don't need to be run as updates.
151
- RE_NON_UPDATE = re.compile(r"^\W*(SELECT)", re.IGNORECASE)
158
+ RE_NON_UPDATE = re.compile(r"^\W*(SELECT|GRAPH|FROM)", re.IGNORECASE)
152
159
 
153
160
  RE_WITH = re.compile(r"^\s*(WITH)", re.IGNORECASE)
154
161
 
@@ -20,7 +20,7 @@ import time
20
20
 
21
21
  from google.cloud.spanner_dbapi.batch_dml_executor import BatchMode
22
22
  from google.cloud.spanner_dbapi.exceptions import RetryAborted
23
- from google.cloud.spanner_v1.session import _get_retry_delay
23
+ from google.cloud.spanner_v1._helpers import _get_retry_delay
24
24
 
25
25
  if TYPE_CHECKING:
26
26
  from google.cloud.spanner_dbapi import Connection, Cursor
@@ -19,6 +19,7 @@ import decimal
19
19
  import math
20
20
  import time
21
21
  import base64
22
+ import threading
22
23
 
23
24
  from google.protobuf.struct_pb2 import ListValue
24
25
  from google.protobuf.struct_pb2 import Value
@@ -26,10 +27,15 @@ from google.protobuf.message import Message
26
27
  from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
27
28
 
28
29
  from google.api_core import datetime_helpers
30
+ from google.api_core.exceptions import Aborted
29
31
  from google.cloud._helpers import _date_from_iso8601_date
30
32
  from google.cloud.spanner_v1 import TypeCode
31
33
  from google.cloud.spanner_v1 import ExecuteSqlRequest
32
34
  from google.cloud.spanner_v1 import JsonObject
35
+ from google.cloud.spanner_v1.request_id_header import with_request_id
36
+ from google.rpc.error_details_pb2 import RetryInfo
37
+
38
+ import random
33
39
 
34
40
  # Validation error messages
35
41
  NUMERIC_MAX_SCALE_ERR_MSG = (
@@ -266,66 +272,69 @@ def _parse_value_pb(value_pb, field_type, field_name, column_info=None):
266
272
  :returns: value extracted from value_pb
267
273
  :raises ValueError: if unknown type is passed
268
274
  """
275
+ decoder = _get_type_decoder(field_type, field_name, column_info)
276
+ return _parse_nullable(value_pb, decoder)
277
+
278
+
279
+ def _get_type_decoder(field_type, field_name, column_info=None):
280
+ """Returns a function that converts a Value protobuf to cell data.
281
+
282
+ :type field_type: :class:`~google.cloud.spanner_v1.types.Type`
283
+ :param field_type: type code for the value
284
+
285
+ :type field_name: str
286
+ :param field_name: column name
287
+
288
+ :type column_info: dict
289
+ :param column_info: (Optional) dict of column name and column information.
290
+ An object where column names as keys and custom objects as corresponding
291
+ values for deserialization. It's specifically useful for data types like
292
+ protobuf where deserialization logic is on user-specific code. When provided,
293
+ the custom object enables deserialization of backend-received column data.
294
+ If not provided, data remains serialized as bytes for Proto Messages and
295
+ integer for Proto Enums.
296
+
297
+ :rtype: a function that takes a single protobuf value as an input argument
298
+ :returns: a function that can be used to extract a value from a protobuf value
299
+ :raises ValueError: if unknown type is passed
300
+ """
301
+
269
302
  type_code = field_type.code
270
- if value_pb.HasField("null_value"):
271
- return None
272
303
  if type_code == TypeCode.STRING:
273
- return value_pb.string_value
304
+ return _parse_string
274
305
  elif type_code == TypeCode.BYTES:
275
- return value_pb.string_value.encode("utf8")
306
+ return _parse_bytes
276
307
  elif type_code == TypeCode.BOOL:
277
- return value_pb.bool_value
308
+ return _parse_bool
278
309
  elif type_code == TypeCode.INT64:
279
- return int(value_pb.string_value)
310
+ return _parse_int64
280
311
  elif type_code == TypeCode.FLOAT64:
281
- if value_pb.HasField("string_value"):
282
- return float(value_pb.string_value)
283
- else:
284
- return value_pb.number_value
312
+ return _parse_float
285
313
  elif type_code == TypeCode.FLOAT32:
286
- if value_pb.HasField("string_value"):
287
- return float(value_pb.string_value)
288
- else:
289
- return value_pb.number_value
314
+ return _parse_float
290
315
  elif type_code == TypeCode.DATE:
291
- return _date_from_iso8601_date(value_pb.string_value)
316
+ return _parse_date
292
317
  elif type_code == TypeCode.TIMESTAMP:
293
- DatetimeWithNanoseconds = datetime_helpers.DatetimeWithNanoseconds
294
- return DatetimeWithNanoseconds.from_rfc3339(value_pb.string_value)
295
- elif type_code == TypeCode.ARRAY:
296
- return [
297
- _parse_value_pb(
298
- item_pb, field_type.array_element_type, field_name, column_info
299
- )
300
- for item_pb in value_pb.list_value.values
301
- ]
302
- elif type_code == TypeCode.STRUCT:
303
- return [
304
- _parse_value_pb(
305
- item_pb, field_type.struct_type.fields[i].type_, field_name, column_info
306
- )
307
- for (i, item_pb) in enumerate(value_pb.list_value.values)
308
- ]
318
+ return _parse_timestamp
309
319
  elif type_code == TypeCode.NUMERIC:
310
- return decimal.Decimal(value_pb.string_value)
320
+ return _parse_numeric
311
321
  elif type_code == TypeCode.JSON:
312
- return JsonObject.from_str(value_pb.string_value)
322
+ return _parse_json
313
323
  elif type_code == TypeCode.PROTO:
314
- bytes_value = base64.b64decode(value_pb.string_value)
315
- if column_info is not None and column_info.get(field_name) is not None:
316
- default_proto_message = column_info.get(field_name)
317
- if isinstance(default_proto_message, Message):
318
- proto_message = type(default_proto_message)()
319
- proto_message.ParseFromString(bytes_value)
320
- return proto_message
321
- return bytes_value
324
+ return lambda value_pb: _parse_proto(value_pb, column_info, field_name)
322
325
  elif type_code == TypeCode.ENUM:
323
- int_value = int(value_pb.string_value)
324
- if column_info is not None and column_info.get(field_name) is not None:
325
- proto_enum = column_info.get(field_name)
326
- if isinstance(proto_enum, EnumTypeWrapper):
327
- return proto_enum.Name(int_value)
328
- return int_value
326
+ return lambda value_pb: _parse_proto_enum(value_pb, column_info, field_name)
327
+ elif type_code == TypeCode.ARRAY:
328
+ element_decoder = _get_type_decoder(
329
+ field_type.array_element_type, field_name, column_info
330
+ )
331
+ return lambda value_pb: _parse_array(value_pb, element_decoder)
332
+ elif type_code == TypeCode.STRUCT:
333
+ element_decoders = [
334
+ _get_type_decoder(item_field.type_, field_name, column_info)
335
+ for item_field in field_type.struct_type.fields
336
+ ]
337
+ return lambda value_pb: _parse_struct(value_pb, element_decoders)
329
338
  else:
330
339
  raise ValueError("Unknown type: %s" % (field_type,))
331
340
 
@@ -351,6 +360,87 @@ def _parse_list_value_pbs(rows, row_type):
351
360
  return result
352
361
 
353
362
 
363
+ def _parse_string(value_pb) -> str:
364
+ return value_pb.string_value
365
+
366
+
367
+ def _parse_bytes(value_pb):
368
+ return value_pb.string_value.encode("utf8")
369
+
370
+
371
+ def _parse_bool(value_pb) -> bool:
372
+ return value_pb.bool_value
373
+
374
+
375
+ def _parse_int64(value_pb) -> int:
376
+ return int(value_pb.string_value)
377
+
378
+
379
+ def _parse_float(value_pb) -> float:
380
+ if value_pb.HasField("string_value"):
381
+ return float(value_pb.string_value)
382
+ else:
383
+ return value_pb.number_value
384
+
385
+
386
+ def _parse_date(value_pb):
387
+ return _date_from_iso8601_date(value_pb.string_value)
388
+
389
+
390
+ def _parse_timestamp(value_pb):
391
+ DatetimeWithNanoseconds = datetime_helpers.DatetimeWithNanoseconds
392
+ return DatetimeWithNanoseconds.from_rfc3339(value_pb.string_value)
393
+
394
+
395
+ def _parse_numeric(value_pb):
396
+ return decimal.Decimal(value_pb.string_value)
397
+
398
+
399
+ def _parse_json(value_pb):
400
+ return JsonObject.from_str(value_pb.string_value)
401
+
402
+
403
+ def _parse_proto(value_pb, column_info, field_name):
404
+ bytes_value = base64.b64decode(value_pb.string_value)
405
+ if column_info is not None and column_info.get(field_name) is not None:
406
+ default_proto_message = column_info.get(field_name)
407
+ if isinstance(default_proto_message, Message):
408
+ proto_message = type(default_proto_message)()
409
+ proto_message.ParseFromString(bytes_value)
410
+ return proto_message
411
+ return bytes_value
412
+
413
+
414
+ def _parse_proto_enum(value_pb, column_info, field_name):
415
+ int_value = int(value_pb.string_value)
416
+ if column_info is not None and column_info.get(field_name) is not None:
417
+ proto_enum = column_info.get(field_name)
418
+ if isinstance(proto_enum, EnumTypeWrapper):
419
+ return proto_enum.Name(int_value)
420
+ return int_value
421
+
422
+
423
+ def _parse_array(value_pb, element_decoder) -> []:
424
+ return [
425
+ _parse_nullable(item_pb, element_decoder)
426
+ for item_pb in value_pb.list_value.values
427
+ ]
428
+
429
+
430
+ def _parse_struct(value_pb, element_decoders):
431
+ return [
432
+ _parse_nullable(item_pb, element_decoders[i])
433
+ for (i, item_pb) in enumerate(value_pb.list_value.values)
434
+ ]
435
+
436
+
437
+ def _parse_nullable(value_pb, decoder):
438
+ if value_pb.HasField("null_value"):
439
+ return None
440
+ else:
441
+ return decoder(value_pb)
442
+
443
+
354
444
  class _SessionWrapper(object):
355
445
  """Base class for objects wrapping a session.
356
446
 
@@ -374,11 +464,29 @@ def _metadata_with_prefix(prefix, **kw):
374
464
  return [("google-cloud-resource-prefix", prefix)]
375
465
 
376
466
 
467
+ def _retry_on_aborted_exception(
468
+ func,
469
+ deadline,
470
+ ):
471
+ """
472
+ Handles retry logic for Aborted exceptions, considering the deadline.
473
+ """
474
+ attempts = 0
475
+ while True:
476
+ try:
477
+ attempts += 1
478
+ return func()
479
+ except Aborted as exc:
480
+ _delay_until_retry(exc, deadline=deadline, attempts=attempts)
481
+ continue
482
+
483
+
377
484
  def _retry(
378
485
  func,
379
486
  retry_count=5,
380
487
  delay=2,
381
488
  allowed_exceptions=None,
489
+ beforeNextRetry=None,
382
490
  ):
383
491
  """
384
492
  Retry a function with a specified number of retries, delay between retries, and list of allowed exceptions.
@@ -395,6 +503,9 @@ def _retry(
395
503
  """
396
504
  retries = 0
397
505
  while retries <= retry_count:
506
+ if retries > 0 and beforeNextRetry:
507
+ beforeNextRetry(retries, delay)
508
+
398
509
  try:
399
510
  return func()
400
511
  except Exception as exc:
@@ -437,3 +548,99 @@ def _metadata_with_leader_aware_routing(value, **kw):
437
548
  List[Tuple[str, str]]: RPC metadata with leader aware routing header
438
549
  """
439
550
  return ("x-goog-spanner-route-to-leader", str(value).lower())
551
+
552
+
553
+ def _delay_until_retry(exc, deadline, attempts):
554
+ """Helper for :meth:`Session.run_in_transaction`.
555
+
556
+ Detect retryable abort, and impose server-supplied delay.
557
+
558
+ :type exc: :class:`google.api_core.exceptions.Aborted`
559
+ :param exc: exception for aborted transaction
560
+
561
+ :type deadline: float
562
+ :param deadline: maximum timestamp to continue retrying the transaction.
563
+
564
+ :type attempts: int
565
+ :param attempts: number of call retries
566
+ """
567
+
568
+ cause = exc.errors[0]
569
+ now = time.time()
570
+ if now >= deadline:
571
+ raise
572
+
573
+ delay = _get_retry_delay(cause, attempts)
574
+ if delay is not None:
575
+ if now + delay > deadline:
576
+ raise
577
+
578
+ time.sleep(delay)
579
+
580
+
581
+ def _get_retry_delay(cause, attempts):
582
+ """Helper for :func:`_delay_until_retry`.
583
+
584
+ :type exc: :class:`grpc.Call`
585
+ :param exc: exception for aborted transaction
586
+
587
+ :rtype: float
588
+ :returns: seconds to wait before retrying the transaction.
589
+
590
+ :type attempts: int
591
+ :param attempts: number of call retries
592
+ """
593
+ if hasattr(cause, "trailing_metadata"):
594
+ metadata = dict(cause.trailing_metadata())
595
+ else:
596
+ metadata = {}
597
+ retry_info_pb = metadata.get("google.rpc.retryinfo-bin")
598
+ if retry_info_pb is not None:
599
+ retry_info = RetryInfo()
600
+ retry_info.ParseFromString(retry_info_pb)
601
+ nanos = retry_info.retry_delay.nanos
602
+ return retry_info.retry_delay.seconds + nanos / 1.0e9
603
+
604
+ return 2**attempts + random.random()
605
+
606
+
607
+ class AtomicCounter:
608
+ def __init__(self, start_value=0):
609
+ self.__lock = threading.Lock()
610
+ self.__value = start_value
611
+
612
+ @property
613
+ def value(self):
614
+ with self.__lock:
615
+ return self.__value
616
+
617
+ def increment(self, n=1):
618
+ with self.__lock:
619
+ self.__value += n
620
+ return self.__value
621
+
622
+ def __iadd__(self, n):
623
+ """
624
+ Defines the inplace += operator result.
625
+ """
626
+ with self.__lock:
627
+ self.__value += n
628
+ return self
629
+
630
+ def __add__(self, n):
631
+ """
632
+ Defines the result of invoking: value = AtomicCounter + addable
633
+ """
634
+ with self.__lock:
635
+ n += self.__value
636
+ return n
637
+
638
+ def __radd__(self, n):
639
+ """
640
+ Defines the result of invoking: value = addable + AtomicCounter
641
+ """
642
+ return self.__add__(n)
643
+
644
+
645
+ def _metadata_with_request_id(*args, **kwargs):
646
+ return with_request_id(*args, **kwargs)