dqlite-dbapi 0.2.0__tar.gz → 0.2.2__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 (515) hide show
  1. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/PKG-INFO +16 -8
  2. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/README.md +13 -5
  3. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/pyproject.toml +3 -3
  4. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/__init__.py +76 -13
  5. dqlite_dbapi-0.2.2/src/dqlitedbapi/_busy_retry.py +297 -0
  6. dqlite_dbapi-0.2.2/src/dqlitedbapi/_pragma_intercept.py +264 -0
  7. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/aio/__init__.py +84 -5
  8. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/aio/connection.py +199 -7
  9. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/aio/cursor.py +134 -20
  10. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/connection.py +551 -67
  11. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/cursor.py +243 -50
  12. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/exceptions.py +27 -0
  13. dqlite_dbapi-0.2.2/src/dqlitedbapi/row.py +93 -0
  14. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/types.py +169 -20
  15. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_executemany_failure_resets_rowcount.py +11 -9
  16. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_misc_coverage.py +23 -15
  17. dqlite_dbapi-0.2.2/tests/integration/test_pragma_deny_list.py +122 -0
  18. dqlite_dbapi-0.2.2/tests/integration/test_session_mode_read_only.py +128 -0
  19. dqlite_dbapi-0.2.2/tests/test_aenter_cleanup_nulls_locks_under_concurrent_close.py +136 -0
  20. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aexit_commit_cancel_debug_log.py +8 -0
  21. dqlite_dbapi-0.2.2/tests/test_aexit_commit_cancel_force_closes_transport.py +137 -0
  22. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aexit_rollback_debug_log.py +8 -0
  23. dqlite_dbapi-0.2.2/tests/test_aio_execute_unlocked_silent_drop_scrubs_state.py +126 -0
  24. dqlite_dbapi-0.2.2/tests/test_aio_force_close_foreign_thread_cursor_closed_pre_cascade.py +123 -0
  25. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_module_attributes.py +4 -1
  26. dqlite_dbapi-0.2.2/tests/test_aio_transaction_recheck_after_ensure_connection.py +96 -0
  27. dqlite_dbapi-0.2.2/tests/test_async_commit_rollback_timeout_outer_scope_hint.py +131 -0
  28. dqlite_dbapi-0.2.2/tests/test_async_resource_warning_sanitize_parity.py +83 -0
  29. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_audit_2026_05_coverage_gaps.py +15 -6
  30. dqlite_dbapi-0.2.2/tests/test_begin_rewrite_session_mode.py +210 -0
  31. dqlite_dbapi-0.2.2/tests/test_busy_retry_async_wiring.py +159 -0
  32. dqlite_dbapi-0.2.2/tests/test_busy_retry_classifier.py +97 -0
  33. dqlite_dbapi-0.2.2/tests/test_busy_retry_curve.py +149 -0
  34. dqlite_dbapi-0.2.2/tests/test_busy_retry_docstring_pins_cross_thread_caveat.py +61 -0
  35. dqlite_dbapi-0.2.2/tests/test_busy_retry_sync_wiring.py +140 -0
  36. dqlite_dbapi-0.2.2/tests/test_check_same_thread_kwarg.py +264 -0
  37. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_close_bypass_path_releases_latched_op_lock.py +5 -1
  38. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_close_signal_handler_reentry.py +7 -2
  39. dqlite_dbapi-0.2.2/tests/test_connect_busy_timeout_kwarg.py +125 -0
  40. dqlite_dbapi-0.2.2/tests/test_connection_docstring_pins_check_same_thread_contract.py +106 -0
  41. dqlite_dbapi-0.2.2/tests/test_convert_params_conform_raise_propagates_unwrapped.py +81 -0
  42. dqlite_dbapi-0.2.2/tests/test_cursor_arraysize_row_factory_setter_thread.py +129 -0
  43. dqlite_dbapi-0.2.2/tests/test_cursors_weakset_state_lock.py +142 -0
  44. dqlite_dbapi-0.2.2/tests/test_dbapi_type_hash_consistency.py +87 -0
  45. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_description_null_first_row_resolves.py +9 -3
  46. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_description_null_type_code_to_none.py +19 -10
  47. dqlite_dbapi-0.2.2/tests/test_description_type_code_unknown_sentinel.py +69 -0
  48. dqlite_dbapi-0.2.2/tests/test_exceptions_all_symmetry.py +43 -0
  49. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_cancel_restores_pre_batch_lastrowid.py +31 -26
  50. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_cancellederror_restores_pre_batch_lastrowid.py +13 -12
  51. dqlite_dbapi-0.2.2/tests/test_executemany_lastrowid_docstring_modern_stdlib.py +41 -0
  52. dqlite_dbapi-0.2.2/tests/test_executemany_lastrowid_mid_batch_preserves_in_batch.py +167 -0
  53. dqlite_dbapi-0.2.2/tests/test_executemany_push_skips_cascade_zeroed_cursor.py +77 -0
  54. dqlite_dbapi-0.2.2/tests/test_format_utc_offset_non_timedelta_guard.py +62 -0
  55. dqlite_dbapi-0.2.2/tests/test_get_async_connection_force_close_race.py +148 -0
  56. dqlite_dbapi-0.2.2/tests/test_in_transaction_property_under_check_same_thread.py +141 -0
  57. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_module_attributes.py +17 -2
  58. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_module_level_stubs.py +20 -1
  59. dqlite_dbapi-0.2.2/tests/test_number_includes_boolean_documented.py +34 -0
  60. dqlite_dbapi-0.2.2/tests/test_pragma_busy_timeout_intercept.py +201 -0
  61. dqlite_dbapi-0.2.2/tests/test_register_adapter_positional_only_signature.py +38 -0
  62. dqlite_dbapi-0.2.2/tests/test_row_factory_sqlite3_equivalent.py +110 -0
  63. dqlite_dbapi-0.2.2/tests/test_rowcount_select_divergence_documented.py +47 -0
  64. dqlite_dbapi-0.2.2/tests/test_sync_close_op_lock_owner_aware_probe.py +100 -0
  65. dqlite_dbapi-0.2.2/tests/test_sync_commit_rollback_snapshot_inner_for_tier2.py +53 -0
  66. dqlite_dbapi-0.2.2/tests/test_sync_transaction_ctxmgr_owner_slot_busy_sentinel.py +55 -0
  67. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_transaction_owner_token_bookkeeping.py +35 -16
  68. dqlite_dbapi-0.2.2/tests/test_text_bind_nul_byte_rejected_with_blob_hint.py +92 -0
  69. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_thread_safety_enforcement.py +19 -8
  70. dqlite_dbapi-0.2.2/tests/test_transaction_ctxmgr_state_lock.py +235 -0
  71. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_types_dbapi_compare.py +22 -21
  72. dqlite_dbapi-0.2.2/tests/test_unregister_adapter.py +93 -0
  73. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_validate_ticks_and_unixtime_reject_bool_and_non_numeric.py +28 -0
  74. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/uv.lock +3 -3
  75. dqlite_dbapi-0.2.0/tests/integration/test_pragma_deny_list.py +0 -60
  76. dqlite_dbapi-0.2.0/tests/test_dbapi_type_hash_consistency.py +0 -77
  77. dqlite_dbapi-0.2.0/tests/test_unregister_adapter.py +0 -50
  78. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/.github/workflows/publish-to-pypi.yml +0 -0
  79. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/.gitignore +0 -0
  80. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/DEVELOPMENT.md +0 -0
  81. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/LICENSE.md +0 -0
  82. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/_constants.py +0 -0
  83. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/src/dqlitedbapi/py.typed +0 -0
  84. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/__init__.py +0 -0
  85. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/aio/test_aio_version_final_annotated.py +0 -0
  86. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/aio/test_async_close_underlying_close_failed_logged.py +0 -0
  87. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/aio/test_async_connection_fork_guard.py +0 -0
  88. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/aio/test_async_iter_protocol_invariants.py +0 -0
  89. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/aio/test_async_unclosed_warning_skips_in_fork_child.py +0 -0
  90. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/aio/test_close_cancel_between_cursor_cascade_and_lock.py +0 -0
  91. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/aio/test_fetchmany_cancel_atomic.py +0 -0
  92. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/aio/test_messages_clear_on_closed.py +0 -0
  93. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/conftest.py +0 -0
  94. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/__init__.py +0 -0
  95. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/conftest.py +0 -0
  96. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_async_concurrent_executemany_serialized.py +0 -0
  97. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_async_cursor_concurrent_execute_rejected.py +0 -0
  98. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_async_cursor_edge_branches.py +0 -0
  99. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_async_fetchmany_cancel.py +0 -0
  100. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_async_returning_zero_rows.py +0 -0
  101. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_async_transaction_ctxmgr_rejects_stray_commit.py +0 -0
  102. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_close_implicit_rollback_pep249.py +0 -0
  103. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_concurrent_cursors_visibility.py +0 -0
  104. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_constraint_extended_codes.py +0 -0
  105. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_context_manager.py +0 -0
  106. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_cursor_fetch_after_non_row_execute.py +0 -0
  107. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_cursor_misc_invariant_pins.py +0 -0
  108. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_cursor_row_index_after_fetchall.py +0 -0
  109. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_datetime_conversion.py +0 -0
  110. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_description_after_ddl_pragma.py +0 -0
  111. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_description_survives_empty_fetchall.py +0 -0
  112. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_executemany_cancel_mid_batch.py +0 -0
  113. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_executemany_completed_iterations_observability.py +0 -0
  114. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_executemany_cte_dml.py +0 -0
  115. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_executemany_generator.py +0 -0
  116. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_executemany_returning.py +0 -0
  117. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_integrity_error.py +0 -0
  118. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_lastrowid_autoincrement.py +0 -0
  119. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_lastrowid_cursor_scoped.py +0 -0
  120. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_lastrowid_returning.py +0 -0
  121. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_lastrowid_survives_cross_cursor_rollback.py +0 -0
  122. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_leader_redirect_live.py +0 -0
  123. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_multiline_returning_rows.py +0 -0
  124. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_nan_inf_float.py +0 -0
  125. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_negative_zero_and_subnormal_float.py +0 -0
  126. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_no_transaction_error_wording.py +0 -0
  127. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_null_vs_empty_text_blob_three_way.py +0 -0
  128. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_query_detection.py +0 -0
  129. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_returning.py +0 -0
  130. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_savepoint_dbapi.py +0 -0
  131. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_savepoint_failure_responses.py +0 -0
  132. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_sqlite_version_pin.py +0 -0
  133. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/integration/test_sync_transaction_ctxmgr_rejects_stray_commit.py +0 -0
  134. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aconn_execute_with_parameters.py +0 -0
  135. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aconn_factory_getters.py +0 -0
  136. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aconnect_cleanup_close_shielded_against_outer_timeout.py +0 -0
  137. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aconnect_cleanup_on_connect_failure.py +0 -0
  138. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_adapter_exceptions_wrapped_as_data_error.py +0 -0
  139. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aenter_cleanup_preserves_original_under_outer_cancel.py +0 -0
  140. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aexit_behavioural_no_close.py +0 -0
  141. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aexit_rollback_cancel_body_context_pin.py +0 -0
  142. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_commit_rollback_no_tx.py +0 -0
  143. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_commit_rollback_no_tx_code_matrix.py +0 -0
  144. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_commit_rollback_wrapping.py +0 -0
  145. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_connection_finalizer_detached_on_cancel_close.py +0 -0
  146. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_connection_finalizer_detached_on_close.py +0 -0
  147. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_cursor_drain_rows.py +0 -0
  148. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_cursor_row_factory_typeerror_wrap.py +0 -0
  149. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_description_tuple_exported.py +0 -0
  150. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_executemany_cursor_close_cascade.py +0 -0
  151. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_force_close_transport_cursor_cascade.py +0 -0
  152. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_init_reexports_diagnostic_prefixes.py +0 -0
  153. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_loop_affinity_exc_class.py +0 -0
  154. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_register_adapter_export.py +0 -0
  155. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_register_converter_stubs.py +0 -0
  156. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_row_factory_setter_loop_binding.py +0 -0
  157. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_aio_setters_clear_messages_pep249.py +0 -0
  158. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_arraysize_validation.py +0 -0
  159. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_backup_not_async_def.py +0 -0
  160. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_check_loop_only_pid_before_loop.py +0 -0
  161. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_close_cancel_arm_cancels_inner_pending_drain.py +0 -0
  162. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_close_ensure_locks_guard.py +0 -0
  163. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_close_finally_propagates_cancel.py +0 -0
  164. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_close_op_lock_bounded.py +0 -0
  165. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_close_outer_cancel_force_closes_transport.py +0 -0
  166. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_close_race.py +0 -0
  167. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_commit_rollback_force_close_on_outer_cancel.py +0 -0
  168. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_commit_rollback_in_transaction_under_lock.py +0 -0
  169. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_commit_rollback_loop_binding_first.py +0 -0
  170. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_commit_rollback_messages_under_lock.py +0 -0
  171. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_commit_rollback_op_lock_bounded.py +0 -0
  172. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_commit_rollback_phase_budget.py +0 -0
  173. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_commit_rollback_retry_after_invalidate.py +0 -0
  174. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_commit_rollback_timeout_phase_aware.py +0 -0
  175. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_connect_cross_loop_diagnostic_on_fast_path.py +0 -0
  176. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_connection.py +0 -0
  177. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_connection_close_during_connect.py +0 -0
  178. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_connection_cursor_fork_guard.py +0 -0
  179. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_connection_ensure_connection_post_yield_close_shielded.py +0 -0
  180. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_connection_race.py +0 -0
  181. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_connection_setters_loop_binding.py +0 -0
  182. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_connection_transaction.py +0 -0
  183. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor.py +0 -0
  184. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_aclose_alias.py +0 -0
  185. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_add_races_cascade_clear.py +0 -0
  186. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_aenter_cross_task_executing_guard.py +0 -0
  187. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_aenter_gc_parent_pep249.py +0 -0
  188. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_aiter_aenter_pep249_pins.py +0 -0
  189. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_aiter_cross_loop_fail_fast.py +0 -0
  190. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_arraysize_row_factory_loop_binding.py +0 -0
  191. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_callproc_nextset_scroll_loop_binding.py +0 -0
  192. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_close_clears_executing_task.py +0 -0
  193. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_close_is_sync_and_race_safe.py +0 -0
  194. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_close_lock_free.py +0 -0
  195. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_closed_state_pep249.py +0 -0
  196. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_execute_executing_task_inside_try_frame.py +0 -0
  197. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_execute_messages_clear.py +0 -0
  198. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_executemany_validation_clears_executing_task.py +0 -0
  199. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_fetch_loop_binding.py +0 -0
  200. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_finally_clear_executing_task_unconditional.py +0 -0
  201. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_late_add_proxy_swap.py +0 -0
  202. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_loop_binding.py +0 -0
  203. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_rownumber_closed_cross_loop.py +0 -0
  204. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_cursor_setinputsizes_loop_binding.py +0 -0
  205. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_executemany_atomic.py +0 -0
  206. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_executemany_close_race.py +0 -0
  207. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_executescript_stub_raises_on_call.py +0 -0
  208. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_fetch_does_not_lazy_bind_loop.py +0 -0
  209. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_fetchall_row_factory_happy_path.py +0 -0
  210. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_force_close_transport.py +0 -0
  211. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_in_transaction_closed_foreign_loop.py +0 -0
  212. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_in_transaction_loop_binding.py +0 -0
  213. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_invalidated_property.py +0 -0
  214. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_lock_binding.py +0 -0
  215. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_resource_warning_gate.py +0 -0
  216. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_secondary_methods_clear_messages_before_guards.py +0 -0
  217. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_transaction_owner_set_inside_try_frame.py +0 -0
  218. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_async_with_exit_does_not_close.py +0 -0
  219. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_audit_2026_05_dbapi_coverage.py +0 -0
  220. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_autocommit_doc_no_round_trip.py +0 -0
  221. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_autocommit_legacy_transaction_control.py +0 -0
  222. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_autocommit_property.py +0 -0
  223. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_autocommit_setter_exact_int_gate.py +0 -0
  224. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_binary_typeerror_stdlib_parity.py +0 -0
  225. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_bind_typeerror_as_dataerror.py +0 -0
  226. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_build_and_connect_base_exception_group_wrap.py +0 -0
  227. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_build_and_connect_resolve_leader_oserror_wrap.py +0 -0
  228. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_build_and_connect_wire_encode_error_translation.py +0 -0
  229. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_call_client_base_exception_group_wrap.py +0 -0
  230. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_call_client_baseexceptiongroup_cancel_split.py +0 -0
  231. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_call_client_docstring_mapping_table.py +0 -0
  232. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_call_client_mapping.py +0 -0
  233. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_call_client_typeerror_message_does_not_blame_bind.py +0 -0
  234. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_call_client_wire_encode_error.py +0 -0
  235. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_callproc_noreturn_annotation.py +0 -0
  236. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_callproc_scroll_clear_messages.py +0 -0
  237. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cascade_cursors_clears_messages.py +0 -0
  238. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_classify_caller_sql.py +0 -0
  239. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_classify_caller_sql_hardening.py +0 -0
  240. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_classify_caller_sql_structural_reject_precedence.py +0 -0
  241. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_classify_operational_code_zero.py +0 -0
  242. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cleanup_loop_thread_disarms_inner_and_reaps_drain.py +0 -0
  243. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cleanup_loop_thread_finalizer_fork_safe.py +0 -0
  244. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_close_resource_leak.py +0 -0
  245. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_close_surfaces_op_lock_contention.py +0 -0
  246. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_close_thread_join_uses_close_timeout.py +0 -0
  247. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_close_timeout_floor_enforcement.py +0 -0
  248. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_close_timeout_plumbing.py +0 -0
  249. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_closed_property.py +0 -0
  250. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cluster_policy_rejection_prefix_constant.py +0 -0
  251. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_code_to_exception_wire_ssot.py +0 -0
  252. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_collated_coverage_gaps.py +0 -0
  253. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_commit_after_external_close.py +0 -0
  254. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_commit_leader_flip_ambiguous_commit_error.py +0 -0
  255. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_commit_leader_flip_propagates.py +0 -0
  256. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_commit_no_spurious_connect.py +0 -0
  257. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_commit_rollback_shortcircuit_uses_in_transaction.py +0 -0
  258. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_conform_adapter_non_primitive_rejected_at_dbapi_layer.py +0 -0
  259. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_conftest_adapter_registry_isolation.py +0 -0
  260. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connect_baseexceptiongroup_cancel_split.py +0 -0
  261. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connect_clears_messages.py +0 -0
  262. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connect_closed_precedes_thread_affinity.py +0 -0
  263. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connect_docstring_parity.py +0 -0
  264. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connect_iso_autocommit_round_trip.py +0 -0
  265. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connect_isolation_level_autocommit_kwargs_symmetric.py +0 -0
  266. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connect_preserves_code.py +0 -0
  267. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection.py +0 -0
  268. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_after_fork_raises.py +0 -0
  269. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_autocommit_isolation_level_closed_state.py +0 -0
  270. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_close_cascades_cursors.py +0 -0
  271. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_close_cursor_cascade_atomicity.py +0 -0
  272. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_close_idempotent_cross_thread.py +0 -0
  273. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_closed_checks.py +0 -0
  274. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_cursor_class_attribute.py +0 -0
  275. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_cursor_closed_state_precedes_unknown_kwarg.py +0 -0
  276. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_database_empty_string_rejected.py +0 -0
  277. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_database_kwarg_isinstance.py +0 -0
  278. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_eager_address_validation.py +0 -0
  279. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_enter_eager.py +0 -0
  280. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_executemany_outer_shape_check.py +0 -0
  281. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_exit_commit_ki_breadcrumb.py +0 -0
  282. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_exit_short_circuits_on_closed.py +0 -0
  283. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_messages_cleared_by_cursor_methods.py +0 -0
  284. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_connection_row_factory_setter_closed_check.py +0 -0
  285. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_context_manager_exit.py +0 -0
  286. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_convert_bind_param_conform_exception_propagates.py +0 -0
  287. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cte_prefix_stripper_edges.py +0 -0
  288. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor.py +0 -0
  289. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_after_external_close.py +0 -0
  290. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_close_clears_messages.py +0 -0
  291. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_close_cross_thread.py +0 -0
  292. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_close_preserves_body_exception_on_messages_strip.py +0 -0
  293. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_close_releases_connection_pin.py +0 -0
  294. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_closed_after_connection_gc_pep249.py +0 -0
  295. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_closed_state_pep249.py +0 -0
  296. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_completed_iterations_resets_on_single_execute.py +0 -0
  297. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_connection_partial_init_attributeerror.py +0 -0
  298. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_connection_property_after_gc_pep249.py +0 -0
  299. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_execute_non_str_operation.py +0 -0
  300. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_execute_state_reset_on_failure.py +0 -0
  301. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_executemany_non_str_operation.py +0 -0
  302. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_executemany_outer_shape_check.py +0 -0
  303. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_factory_kwarg_rejected.py +0 -0
  304. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_force_close_race.py +0 -0
  305. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_iter_clears_messages_pep249.py +0 -0
  306. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_iterator_reset.py +0 -0
  307. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_kwargs_rejection_precedence.py +0 -0
  308. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_next_row_factory_raise_replays_row.py +0 -0
  309. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_per_row_types.py +0 -0
  310. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_post_close_property_reads.py +0 -0
  311. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_protocol_check.py +0 -0
  312. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_rejects_generator_params.py +0 -0
  313. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_repr_with_gced_parent_connection.py +0 -0
  314. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_row_factory_rejecting_setter.py +0 -0
  315. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_row_factory_subclass_inheritance.py +0 -0
  316. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_row_index_reset.py +0 -0
  317. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_rowcount_after_ddl_stdlib_parity.py +0 -0
  318. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_rownumber_affinity.py +0 -0
  319. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_rownumber_post_close.py +0 -0
  320. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_scroll_mode_validation.py +0 -0
  321. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_secondary_methods_positional_only.py +0 -0
  322. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_setinputsizes_affinity_precedes_shape.py +0 -0
  323. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_setinputsizes_setoutputsize_closed_state_precedes_validation.py +0 -0
  324. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_setters_clear_messages_pep249.py +0 -0
  325. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_stop_iteration_contract.py +0 -0
  326. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_cursor_uses_run_protocol.py +0 -0
  327. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_date_widens_to_datetime.py +0 -0
  328. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_datetime_from_iso8601_docstring_two_step_fallback.py +0 -0
  329. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_datetime_time_round_trip.py +0 -0
  330. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dbapi_commit_deferred_fk.py +0 -0
  331. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dbapi_default_timeouts_use_client_constants.py +0 -0
  332. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dbapi_exception_message_arg_cap.py +0 -0
  333. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dbapi_exception_raw_message_cap.py +0 -0
  334. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dbapi_fork_guard_cluster.py +0 -0
  335. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dbapi_hardening.py +0 -0
  336. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_description_strict_type_codes.py +0 -0
  337. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_description_tuple_public_alias.py +0 -0
  338. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_diagnostic_messages.py +0 -0
  339. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dial_attempt_timeout_propagation.py +0 -0
  340. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dial_func_propagation.py +0 -0
  341. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dial_func_propagation_to_leader_resolve.py +0 -0
  342. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_doc_class_and_description_drift.py +0 -0
  343. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_dqlite_namespace_error_classification.py +0 -0
  344. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_ensure_loop_fast_path_snapshot_dcl_defence.py +0 -0
  345. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_ensure_loop_post_fork_pid_guard.py +0 -0
  346. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_error_code_mapping.py +0 -0
  347. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_error_pickle_cause_either_or.py +0 -0
  348. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_error_pickle_preserves_raw_message.py +0 -0
  349. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_exception_coded_mixin.py +0 -0
  350. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_exception_mapping.py +0 -0
  351. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_exception_repr_code.py +0 -0
  352. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_exception_wrapping.py +0 -0
  353. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_exceptions.py +0 -0
  354. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_execute_caller_param_shape_preserves_prior_state.py +0 -0
  355. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_execute_executemany_positional_only.py +0 -0
  356. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_execute_multi_statement_rejection.py +0 -0
  357. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_execute_reject_scrubs_prior_result_state.py +0 -0
  358. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_accumulator_semantics.py +0 -0
  359. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_basecaught_state_reset.py +0 -0
  360. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_classifier_resets_state_first.py +0 -0
  361. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_classify_caller_sql_hoisted.py +0 -0
  362. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_completed_iterations_snapshot_conditional_restore.py +0 -0
  363. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_dict_subclass_accepted.py +0 -0
  364. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_empty.py +0 -0
  365. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_empty_batch_preserves_lastrowid.py +0 -0
  366. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_none_seq_rejected.py +0 -0
  367. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_per_iter_structural_rejects.py +0 -0
  368. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_pragma_with_leading_comment.py +0 -0
  369. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_reject_scrubs_prior_result_state.py +0 -0
  370. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_rejects_pure_queries.py +0 -0
  371. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_rejects_transaction_verbs.py +0 -0
  372. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_executemany_returning_cap.py +0 -0
  373. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_exit_logs_rollback_failure.py +0 -0
  374. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fetchall_row_factory_raise_replay.py +0 -0
  375. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fetchmany_edges.py +0 -0
  376. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fetchmany_negative_size_rejected.py +0 -0
  377. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fetchmany_non_int_validation.py +0 -0
  378. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fetchmany_one_clear_per_call_pep249.py +0 -0
  379. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fetchmany_row_factory_raise_replay.py +0 -0
  380. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fetchmany_zero_docstring_stdlib_divergence.py +0 -0
  381. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_finalizer_strict_warnings.py +0 -0
  382. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_cancel_and_observe_narrow_suppress.py +0 -0
  383. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_observe_ast_pin.py +0 -0
  384. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_observe_callback_coverage.py +0 -0
  385. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_transport_cleanup_tail_unconditional.py +0 -0
  386. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_transport_clears_messages.py +0 -0
  387. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_transport_cross_thread_cancel_threadsafe.py +0 -0
  388. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_transport_cursor_cascade_threadsafe.py +0 -0
  389. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_transport_defensive_arms.py +0 -0
  390. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_transport_disarms_inner_finalizer.py +0 -0
  391. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_transport_pending_drain_resnapshot.py +0 -0
  392. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_force_close_transport_writer_cross_thread.py +0 -0
  393. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fork_diagnostic_includes_pids.py +0 -0
  394. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_fromticks_tz_policy.py +0 -0
  395. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_gc_cleanup.py +0 -0
  396. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_get_resolve_leader_cluster_no_loop_raises_runtime_error.py +0 -0
  397. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_in_transaction_after_close.py +0 -0
  398. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_in_transaction_closed_divergence_doc.py +0 -0
  399. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_in_transaction_closed_foreign_thread.py +0 -0
  400. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_in_transaction_property.py +0 -0
  401. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_init_docstring_fromticks_three_impls.py +0 -0
  402. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_is_multi_statement_walker.py +0 -0
  403. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_is_no_transaction_error.py +0 -0
  404. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_is_row_returning_multiline_whitespace.py +0 -0
  405. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_is_row_returning_sql_noise.py +0 -0
  406. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iso8601_aware_microseconds_round_trip.py +0 -0
  407. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iso8601_decoder_leap_seconds_rejected.py +0 -0
  408. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iso8601_decoder_subsecond_truncation.py +0 -0
  409. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iso8601_encoder.py +0 -0
  410. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iso8601_encoder_fold.py +0 -0
  411. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iso8601_encoder_utcoffset_exception_wrap.py +0 -0
  412. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iso8601_parse_error_truncation.py +0 -0
  413. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iso8601_tzinfo_exception_classes_wrapped.py +0 -0
  414. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_isolation_level_stdlib_accept_set.py +0 -0
  415. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_iterdump_stub_signature_3_13_filter_kwarg.py +0 -0
  416. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_lastrowid_docstring_stdlib_parity_claim.py +0 -0
  417. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_lastrowid_insert_only.py +0 -0
  418. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_lastrowid_signed_int64_cast.py +0 -0
  419. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_leader_error_codes_identity.py +0 -0
  420. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_leader_redirect_on_connect.py +0 -0
  421. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_legacy_transaction_control_constant.py +0 -0
  422. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_max_continuation_frames_upper_bound.py +0 -0
  423. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_max_message_size_round_trip.py +0 -0
  424. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_max_total_rows_plumbing.py +0 -0
  425. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_max_total_rows_validation.py +0 -0
  426. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_messages_attribute.py +0 -0
  427. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_messages_clear_on_ctxmgr_entry.py +0 -0
  428. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_nextset_noreturn_annotation.py +0 -0
  429. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_no_transaction_substrings_source_of_truth.py +0 -0
  430. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_no_tx_primary_codes_invariant.py +0 -0
  431. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_op_lock_bounded_acquire.py +0 -0
  432. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_operational_error_classification.py +0 -0
  433. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_operational_error_raw_message.py +0 -0
  434. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_param_conversion.py +0 -0
  435. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_param_validation.py +0 -0
  436. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_paramstyle_qmark_only_pin.py +0 -0
  437. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_parse_decltypes_colnames_constants.py +0 -0
  438. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_database_error_routing.py +0 -0
  439. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_exception_aliases.py +0 -0
  440. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_messages_clearing.py +0 -0
  441. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_messages_independence.py +0 -0
  442. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_misc_pins.py +0 -0
  443. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_stub_hasattr_divergence.py +0 -0
  444. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_type_constructors_vs_singletons.py +0 -0
  445. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_type_constructors_wrap_valueerror.py +0 -0
  446. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pep249_type_objects_final_annotated.py +0 -0
  447. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pickle_guard.py +0 -0
  448. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pragma_write_description_none.py +0 -0
  449. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_prepare_protocol_in_types_all.py +0 -0
  450. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_property_round_trip_and_fork_guard.py +0 -0
  451. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_property_setters_do_not_mirror_to_inner_async_conn.py +0 -0
  452. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_protocol_serialization.py +0 -0
  453. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_pyproject_dependency_graph.py +0 -0
  454. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_readme_truthfulness_pins.py +0 -0
  455. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_register_adapter_conform_fallback.py +0 -0
  456. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_register_adapter_global_sync_aio_symmetry.py +0 -0
  457. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_repr.py +0 -0
  458. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_repr_sanitizes_address.py +0 -0
  459. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_resolve_leader_cache_dial_func_id_recycle.py +0 -0
  460. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_resolve_leader_cache_lock_atfork_hook.py +0 -0
  461. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_resolve_leader_cluster_cache.py +0 -0
  462. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_resolve_leader_cross_loop_isolation.py +0 -0
  463. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_row_factory_annotation.py +0 -0
  464. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_row_factory_sqlite3_row_surfaces_dataerror.py +0 -0
  465. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_rowid_number_overlap_documented.py +0 -0
  466. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_rownumber_closed_cursor_returns_none_documented.py +0 -0
  467. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_ensure_loop_raise_closes_coroutine.py +0 -0
  468. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_keyboard_interrupt.py +0 -0
  469. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_keyboard_interrupt_cleanup_paths.py +0 -0
  470. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_ki_post_schedule_pre_await.py +0 -0
  471. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_ki_race_recovery.py +0 -0
  472. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_loop_closed_during_schedule.py +0 -0
  473. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_other_runtimeerror_propagates.py +0 -0
  474. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_timeout.py +0 -0
  475. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_run_sync_timeout_recovery_preserves_conn.py +0 -0
  476. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_secondary_methods_clear_messages_before_thread_check.py +0 -0
  477. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_setinputsizes_accepts_none.py +0 -0
  478. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_setinputsizes_clears_messages_per_pep249.py +0 -0
  479. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_setinputsizes_non_sequence_iterable.py +0 -0
  480. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_setinputsizes_sequence_acceptance.py +0 -0
  481. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_setinputsizes_short_circuit_on_closed_connection.py +0 -0
  482. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_setoutputsize_accepts_none.py +0 -0
  483. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_setter_closed_state_precedes_thread.py +0 -0
  484. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sqlite_errorname_property.py +0 -0
  485. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sqlite_version_not_public_attribute.py +0 -0
  486. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_strip_leading_comments_bom.py +0 -0
  487. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_strip_leading_comments_parity.py +0 -0
  488. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_strip_sql_noise_backtick_identifier.py +0 -0
  489. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_stub_signatures_normalize_to_dbapi_error.py +0 -0
  490. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_submodule_all.py +0 -0
  491. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_commit_rollback_closed_precedes_thread.py +0 -0
  492. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_commit_rollback_inside_ctxmgr_guard.py +0 -0
  493. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_commit_rollback_messages_under_lock.py +0 -0
  494. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_commit_rollback_retry_after_invalidate.py +0 -0
  495. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_connection_execute_shortcut.py +0 -0
  496. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_connection_executemany_shortcut.py +0 -0
  497. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_cursor_post_add_closed_recheck.py +0 -0
  498. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_exit_signal_mid_rollback.py +0 -0
  499. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_fetchmany_cancel_atomic.py +0 -0
  500. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_force_close_transport.py +0 -0
  501. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_force_close_transport_pending_drain_reap.py +0 -0
  502. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_invalidated_property.py +0 -0
  503. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_sync_run_sync_multiphase_timeout.py +0 -0
  504. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_timeout_validation.py +0 -0
  505. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_top_level_logger_has_null_handler.py +0 -0
  506. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_total_changes_hasattr_safe.py +0 -0
  507. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_transitive_wire_import.py +0 -0
  508. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_type_coercion_documented_asymmetries.py +0 -0
  509. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_types_validator_branches.py +0 -0
  510. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_unregister_adapter_rejects_built_in_datetime_types.py +0 -0
  511. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_unsupported_method_stubs.py +0 -0
  512. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_validate_positive_int_pep249_wrap.py +0 -0
  513. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_validate_ticks.py +0 -0
  514. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_version.py +0 -0
  515. {dqlite_dbapi-0.2.0 → dqlite_dbapi-0.2.2}/tests/test_version_export.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dqlite-dbapi
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: PEP 249 (DB-API 2.0) compliant interface for dqlite
5
5
  Project-URL: Homepage, https://github.com/letsdiscodev/python-dqlite-dbapi
6
6
  Project-URL: Repository, https://github.com/letsdiscodev/python-dqlite-dbapi
@@ -22,8 +22,8 @@ Classifier: Topic :: Database :: Database Engines/Servers
22
22
  Classifier: Topic :: Database :: Front-Ends
23
23
  Classifier: Typing :: Typed
24
24
  Requires-Python: >=3.13
25
- Requires-Dist: dqlite-client<0.3.0,>=0.2.0
26
- Requires-Dist: dqlite-wire<0.3.0,>=0.2.0
25
+ Requires-Dist: dqlite-client<0.3.0,>=0.2.2
26
+ Requires-Dist: dqlite-wire<0.3.0,>=0.2.2
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: mypy>=1.0; extra == 'dev'
29
29
  Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
@@ -73,7 +73,9 @@ asyncio.run(main())
73
73
  ## PEP 249 Compliance
74
74
 
75
75
  - `apilevel = "2.0"`
76
- - `threadsafety = 1`
76
+ - `threadsafety = 2` (capability ceiling; default `check_same_thread=True`
77
+ enforces strict per-thread — matches stdlib sqlite3's "advertise
78
+ ceiling, default to floor" pattern)
77
79
  - `paramstyle = "qmark"`
78
80
 
79
81
  ## Transactions
@@ -240,10 +242,16 @@ borrowed from one.
240
242
  stdlib silently accepts empty / whitespace-only / comment-only SQL
241
243
  as a no-op; the driver pre-flight rejects per PEP 249 §7 to surface
242
244
  caller bugs at the call site.
243
- - **`threadsafety = 1`** (stdlib reports `3`). Each Connection is
244
- thread-affine: methods called from a foreign thread raise
245
- `ProgrammingError`. Use one Connection per thread or use the async
246
- surface for a single-thread-per-loop model.
245
+ - **`threadsafety = 2`** (stdlib reports `3` we don't claim Tier 3
246
+ because cursors aren't shareable per the documented "share
247
+ connections, not cursors" contract). Tier 2 = "threads may share
248
+ the module AND connections"; matches the dbapi's
249
+ ``check_same_thread=False`` opt-in behaviour. Default
250
+ ``check_same_thread=True`` enforces strict per-thread; methods
251
+ called from a foreign thread on a default-mode Connection raise
252
+ ``ProgrammingError``. Tier 3 (cursor sharing) is tracked as
253
+ future work in
254
+ ``issues/dbapi-threadsafety-tier-3-cursor-sharing-stdlib-parity.md``.
247
255
  - **No `executescript` / `create_function` / `create_aggregate` /
248
256
  `create_window_function` / `iterdump` / `backup` / `set_authorizer`
249
257
  / `serialize` / `blobopen`.** stdlib-specific APIs that have no
@@ -39,7 +39,9 @@ asyncio.run(main())
39
39
  ## PEP 249 Compliance
40
40
 
41
41
  - `apilevel = "2.0"`
42
- - `threadsafety = 1`
42
+ - `threadsafety = 2` (capability ceiling; default `check_same_thread=True`
43
+ enforces strict per-thread — matches stdlib sqlite3's "advertise
44
+ ceiling, default to floor" pattern)
43
45
  - `paramstyle = "qmark"`
44
46
 
45
47
  ## Transactions
@@ -206,10 +208,16 @@ borrowed from one.
206
208
  stdlib silently accepts empty / whitespace-only / comment-only SQL
207
209
  as a no-op; the driver pre-flight rejects per PEP 249 §7 to surface
208
210
  caller bugs at the call site.
209
- - **`threadsafety = 1`** (stdlib reports `3`). Each Connection is
210
- thread-affine: methods called from a foreign thread raise
211
- `ProgrammingError`. Use one Connection per thread or use the async
212
- surface for a single-thread-per-loop model.
211
+ - **`threadsafety = 2`** (stdlib reports `3` we don't claim Tier 3
212
+ because cursors aren't shareable per the documented "share
213
+ connections, not cursors" contract). Tier 2 = "threads may share
214
+ the module AND connections"; matches the dbapi's
215
+ ``check_same_thread=False`` opt-in behaviour. Default
216
+ ``check_same_thread=True`` enforces strict per-thread; methods
217
+ called from a foreign thread on a default-mode Connection raise
218
+ ``ProgrammingError``. Tier 3 (cursor sharing) is tracked as
219
+ future work in
220
+ ``issues/dbapi-threadsafety-tier-3-cursor-sharing-stdlib-parity.md``.
213
221
  - **No `executescript` / `create_function` / `create_aggregate` /
214
222
  `create_window_function` / `iterdump` / `backup` / `set_authorizer`
215
223
  / `serialize` / `blobopen`.** stdlib-specific APIs that have no
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dqlite-dbapi"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "PEP 249 (DB-API 2.0) compliant interface for dqlite"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -26,8 +26,8 @@ classifiers = [
26
26
  "Typing :: Typed",
27
27
  ]
28
28
  dependencies = [
29
- "dqlite-client>=0.2.0,<0.3.0",
30
- "dqlite-wire>=0.2.0,<0.3.0",
29
+ "dqlite-client>=0.2.2,<0.3.0",
30
+ "dqlite-wire>=0.2.2,<0.3.0",
31
31
  ]
32
32
 
33
33
  [tool.uv.sources]
@@ -71,6 +71,7 @@ from dqlitedbapi.connection import (
71
71
  )
72
72
  from dqlitedbapi.cursor import Cursor
73
73
  from dqlitedbapi.exceptions import (
74
+ AdapterLookupError,
74
75
  AmbiguousCommitError,
75
76
  DatabaseError,
76
77
  DataError,
@@ -83,12 +84,14 @@ from dqlitedbapi.exceptions import (
83
84
  ProgrammingError,
84
85
  Warning,
85
86
  )
87
+ from dqlitedbapi.row import Row
86
88
  from dqlitedbapi.types import (
87
89
  BINARY,
88
90
  DATETIME,
89
91
  NUMBER,
90
92
  ROWID,
91
93
  STRING,
94
+ UNKNOWN,
92
95
  Binary,
93
96
  Date,
94
97
  DateFromTicks,
@@ -107,14 +110,38 @@ from dqlitewire import (
107
110
 
108
111
  # PEP 249 module-level attributes
109
112
  apilevel: _Final[_Literal["2.0"]] = "2.0"
110
- # PEP 249 value 1: threads may share the module.
113
+ # PEP 249 value 2: threads may share the module and connections.
111
114
  #
112
- # This driver is stricter than the PEP minimum: each Connection is
113
- # bound to the thread that created it. Any method call from a
114
- # different thread raises ProgrammingError. Use one Connection per
115
- # thread, or use the async API (dqlitedbapi.aio.aconnect) for a
116
- # single-thread-per-loop model.
117
- threadsafety: _Final[_Literal[1]] = 1
115
+ # This declares the driver's CAPABILITY ceiling: connections are
116
+ # safe to share across threads once the user opts in via
117
+ # ``check_same_thread=False`` at ``connect()`` (matches stdlib
118
+ # sqlite3's convention stdlib advertises ``threadsafety = 3``
119
+ # while defaulting ``check_same_thread=True`` to enforce strict
120
+ # per-thread at the Python layer). Cursors are explicitly NOT
121
+ # shareable across threads — the documented contract is "share
122
+ # connections, not cursors" (see ``Connection`` docstring); the
123
+ # ceiling stays at PEP 249 tier 2 for that reason. Tier 3 (cursors
124
+ # shareable too) is tracked as future work in
125
+ # ``issues/dbapi-threadsafety-tier-3-cursor-sharing-stdlib-parity.md``.
126
+ #
127
+ # DEFAULT enforcement is still per-thread: ``check_same_thread``
128
+ # defaults to ``True`` and ``Connection._check_thread()`` raises
129
+ # ``ProgrammingError`` on cross-thread method calls. Pass
130
+ # ``check_same_thread=False`` to use the connection sharing
131
+ # advertised here. The wire is always serialised by ``_op_lock``;
132
+ # the Phase 2 hardening (``_state_lock`` for transaction owner
133
+ # read-check-reserve, ``_cursors`` WeakSet add/discard) closes
134
+ # the cross-thread correctness gaps for the connection-shared
135
+ # path.
136
+ #
137
+ # This declaration uses the "advertise ceiling, default to floor"
138
+ # pattern stdlib sqlite3 established since CPython 3.11
139
+ # (bpo-45613 / gh-31464 made ``sqlite3.threadsafety`` dynamic via
140
+ # ``SQLITE_THREADSAFE`` mode; serialized = tier 3). The pattern
141
+ # describes capability rather than default enforcement;
142
+ # capability-detection libraries reading this value learn the
143
+ # safe-with-opt-in ceiling.
144
+ threadsafety: _Final[_Literal[2]] = 2
118
145
  paramstyle: _Final[_Literal["qmark"]] = "qmark" # Question mark style: WHERE name=?
119
146
 
120
147
  # SQLite compatibility attributes (for SQLAlchemy).
@@ -147,7 +174,7 @@ LEGACY_TRANSACTION_CONTROL: _Final[int] = -1
147
174
  PARSE_DECLTYPES: _Final[int] = 1
148
175
  PARSE_COLNAMES: _Final[int] = 2
149
176
 
150
- __version__: _Final[str] = "0.2.0"
177
+ __version__: _Final[str] = "0.2.2"
151
178
 
152
179
  __all__ = [ # grouped by PEP 249 section, not alphabetical
153
180
  # Module attributes
@@ -190,6 +217,7 @@ __all__ = [ # grouped by PEP 249 section, not alphabetical
190
217
  "InternalError",
191
218
  "ProgrammingError",
192
219
  "NotSupportedError",
220
+ "AdapterLookupError",
193
221
  # Type constructors
194
222
  "Date",
195
223
  "Time",
@@ -204,6 +232,9 @@ __all__ = [ # grouped by PEP 249 section, not alphabetical
204
232
  "NUMBER",
205
233
  "DATETIME",
206
234
  "ROWID",
235
+ "UNKNOWN",
236
+ # Row factory (sqlite3.Row equivalent)
237
+ "Row",
207
238
  # Type aliases
208
239
  "DescriptionTuple",
209
240
  ]
@@ -222,6 +253,9 @@ def connect(
222
253
  dial_timeout: float | None = None,
223
254
  attempt_timeout: float | None = None,
224
255
  dial_func: DialFunc | None = None,
256
+ busy_timeout: float = 5.0,
257
+ check_same_thread: bool = True,
258
+ session_mode: str | None = None,
225
259
  **unknown_kwargs: object,
226
260
  ) -> Connection:
227
261
  """Connect to a dqlite database.
@@ -269,6 +303,33 @@ def connect(
269
303
  ``asyncio.open_connection`` path. Forwarded to the
270
304
  underlying :class:`Connection`. See
271
305
  :data:`dqliteclient.DialFunc` for the protocol.
306
+ busy_timeout: Maximum cumulative seconds to spend retrying
307
+ BUSY responses before raising. Default ``5.0`` matches
308
+ stdlib ``sqlite3.connect(timeout=5.0)``. Retries follow
309
+ SQLite's deterministic ``sqliteDefaultBusyCallback``
310
+ curve (1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 ms
311
+ then flat 100 ms). ``0`` disables retry (first BUSY
312
+ raises immediately — stdlib parity). dqlite's VFS
313
+ authorizer rejects the canonical ``PRAGMA busy_timeout``
314
+ on the server side; this driver intercepts the PRAGMA at
315
+ the Cursor layer so ``cur.execute("PRAGMA busy_timeout
316
+ = 30000")`` works transparently and updates the same
317
+ backing field as the kwarg.
318
+ check_same_thread: When ``True`` (default), every method
319
+ call on the returned Connection must come from the
320
+ thread that called ``connect()``; cross-thread calls
321
+ raise ``ProgrammingError``. When ``False``, the cross-
322
+ thread check is gated off and the Connection can be
323
+ shared across threads — the wire is already serialised
324
+ by the per-Connection op-lock regardless of caller
325
+ thread. Per-cursor result state is NOT thread-safe; the
326
+ documented pattern is "share connections, not cursors."
327
+ Matches stdlib ``sqlite3.connect(check_same_thread=
328
+ False)``. The fork check is NEVER relaxed; cross-process
329
+ Connection use raises ``InterfaceError`` in both modes.
330
+ See ``dqlitedbapi.Connection`` docstring for the
331
+ cursor / messages / close() contracts under the
332
+ relaxation.
272
333
 
273
334
  Returns:
274
335
  A Connection object
@@ -323,11 +384,10 @@ def connect(
323
384
  f"(stdlib LEGACY_TRANSACTION_CONTROL) only; got {autoc!r}"
324
385
  )
325
386
 
326
- # Reject stdlib ``sqlite3.connect`` kwargs that this driver
327
- # cannot honour (``detect_types``, ``check_same_thread``,
328
- # ``factory``, ``cached_statements``, ``uri``) with
329
- # ``NotSupportedError`` rather than letting Python's call-
330
- # protocol leak ``TypeError`` (which escapes ``except
387
+ # Reject other stdlib ``sqlite3.connect`` kwargs that this driver
388
+ # cannot honour (``detect_types``, ``factory``, ``cached_statements``,
389
+ # ``uri``) with ``NotSupportedError`` rather than letting Python's
390
+ # call-protocol leak ``TypeError`` (which escapes ``except
331
391
  # dbapi.Error:``). Cross-driver code that passes stdlib kwargs
332
392
  # through should be able to catch the rejection inside the
333
393
  # dbapi error hierarchy.
@@ -352,6 +412,9 @@ def connect(
352
412
  dial_timeout=dial_timeout,
353
413
  attempt_timeout=attempt_timeout,
354
414
  dial_func=dial_func,
415
+ busy_timeout=busy_timeout,
416
+ check_same_thread=check_same_thread,
417
+ session_mode=session_mode,
355
418
  )
356
419
  # Apply the validated ``isolation_level`` / ``autocommit`` kwargs
357
420
  # via the setters on the freshly-constructed connection. Stdlib
@@ -0,0 +1,297 @@
1
+ """Server-BUSY retry mechanism — stdlib ``sqlite3`` parity.
2
+
3
+ Stdlib ``sqlite3.connect(timeout=N)`` defaults to a 5-second
4
+ ``busy_timeout`` at the SQLite C library level. When a write hits
5
+ contention the C library transparently sleeps and retries via a
6
+ deterministic curve (``main.c::sqliteDefaultBusyCallback``). dqlite's
7
+ server-side VFS authorizer denies ``PRAGMA busy_timeout`` (see
8
+ ``done/dbapi-pragma-deny-list-no-regression-pin.md``), so callers
9
+ cannot tune the retry behaviour through the canonical SQLite escape
10
+ hatch. Without an in-driver retry, application code that worked
11
+ against stdlib ``sqlite3`` silently regresses on dqlite — two
12
+ concurrent writers contending for the dqlite write lock produce
13
+ ``OperationalError: database is locked to <addr>`` surfacing as HTTP
14
+ 500s.
15
+
16
+ This module hosts the SQLite-equivalent retry curve and the
17
+ ``OperationalError`` ↔ BUSY classifier so the sync and async surfaces
18
+ can share both. The actual retry-loop helpers live on the
19
+ ``Connection`` / ``AsyncConnection`` classes (they need access to the
20
+ per-connection ``busy_timeout`` and the run-sync / run-async
21
+ bridges).
22
+
23
+ Design notes:
24
+
25
+ - The curve is SQLite's own (``delays`` / ``totals`` arrays from
26
+ ``main.c``). Battle-tested. No jitter — BUSY is local contention,
27
+ not distributed-systems flapping; jitter would only smear the
28
+ retry burst across siblings without changing the convergence shape.
29
+
30
+ - The classifier gates on ``code == SQLITE_BUSY`` not on a substring
31
+ scan. The user-supplied brief referenced
32
+ ``dqliteclient/connection.py:_RAFT_BUSY_MESSAGE_FRAGMENTS`` but
33
+ that tuple is narrow (``("checkpoint in progress",)``) and is the
34
+ classifier for the SEPARATE "checkpoint-BUSY rewrap as
35
+ ``DqliteConnectionError``" path — NOT the RAFT-BUSY-"is locked"
36
+ family this retry targets. Code-based gating catches both engine-
37
+ BUSY and RAFT-BUSY shapes per the existing comment at
38
+ ``dqliteclient/connection.py:2683-2698``.
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ import asyncio
44
+ import logging
45
+ import time
46
+ from collections.abc import Awaitable, Callable, Coroutine
47
+ from typing import Any, Final
48
+
49
+ from dqlitedbapi.exceptions import OperationalError as _OperationalError
50
+ from dqlitewire import SQLITE_BUSY as _SQLITE_BUSY
51
+
52
+ logger = logging.getLogger(__name__)
53
+
54
+
55
+ def _resolve_busy_timeout_seconds(connection: object) -> float:
56
+ """Read ``connection._busy_timeout`` with a MagicMock-safe
57
+ fallback. Test fixtures construct ``Cursor`` instances with
58
+ ``MagicMock`` connections whose attributes auto-spawn — without
59
+ this guard the retry curve's ``int(busy_timeout * 1000)`` would
60
+ crash on a non-numeric MagicMock. ``isinstance`` check on the
61
+ return value falls through to ``0.0`` (no retry — preserves the
62
+ pre-feature test semantics)."""
63
+ value = getattr(connection, "_busy_timeout", 0.0)
64
+ # ``bool`` is ``int`` to ``isinstance`` but must be rejected
65
+ # explicitly; production code rejects bool at validation. A
66
+ # MagicMock attribute is not a real number — fall through.
67
+ if not isinstance(value, (int, float)) or isinstance(value, bool):
68
+ return 0.0
69
+ return float(value)
70
+
71
+
72
+ # SQLite's busy-callback curve (``main.c::sqliteDefaultBusyCallback``).
73
+ # ``delays[i]`` is the milliseconds to sleep before retry attempt
74
+ # ``i+1``; ``totals[i]`` is the cumulative sleep BEFORE attempt
75
+ # ``i`` (so ``totals[i+1] == totals[i] + delays[i]``). After
76
+ # ``len(delays)`` attempts the curve flattens at the last
77
+ # ``delays`` value (100 ms) — see ``next_busy_delay_ms`` below.
78
+ #
79
+ # Pinned to the canonical SQLite values (verified internally
80
+ # consistent via the totals[i+1] = totals[i] + delays[i] check
81
+ # in ``tests/test_busy_retry_curve.py``). The constant 25 appears
82
+ # three times in a row at positions 6, 7, 8 — this is intentional
83
+ # in the upstream curve, not an editing mistake.
84
+ _BUSY_DELAYS_MS: Final[tuple[int, ...]] = (1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100)
85
+ _BUSY_TOTALS_MS: Final[tuple[int, ...]] = (0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228)
86
+
87
+
88
+ def next_busy_delay_ms(count: int, busy_timeout_ms: int) -> int | None:
89
+ """Return the sleep duration (ms) before retry attempt ``count+1``,
90
+ or ``None`` when the cumulative budget is exhausted.
91
+
92
+ Mirrors SQLite's ``sqliteDefaultBusyCallback`` exactly:
93
+
94
+ - For ``count < len(_BUSY_DELAYS_MS)``, return the next entry from
95
+ the curve.
96
+ - For ``count >= len(_BUSY_DELAYS_MS)``, return the flat tail
97
+ value (100 ms).
98
+ - If the resulting ``prior + delay`` would overrun
99
+ ``busy_timeout_ms``, clamp ``delay`` to fit; if the clamp would
100
+ produce ``delay <= 0``, return ``None`` (caller raises the
101
+ original BUSY).
102
+
103
+ ``busy_timeout_ms <= 0`` short-circuits ``None`` regardless of
104
+ ``count`` — stdlib parity for ``timeout=0`` meaning "no wait."
105
+ """
106
+ if busy_timeout_ms <= 0:
107
+ return None
108
+ n_curve = len(_BUSY_DELAYS_MS)
109
+ if count < n_curve:
110
+ delay = _BUSY_DELAYS_MS[count]
111
+ prior = _BUSY_TOTALS_MS[count]
112
+ else:
113
+ delay = _BUSY_DELAYS_MS[-1]
114
+ prior = _BUSY_TOTALS_MS[-1] + delay * (count - (n_curve - 1))
115
+ if prior + delay > busy_timeout_ms:
116
+ delay = busy_timeout_ms - prior
117
+ if delay <= 0:
118
+ return None
119
+ return delay
120
+
121
+
122
+ def is_busy_exception(exc: BaseException) -> bool:
123
+ """Classify an exception as a retryable BUSY.
124
+
125
+ The retry mechanism gates on ``OperationalError`` whose ``code`` is
126
+ ``SQLITE_BUSY (5)``. This covers both engine-BUSY (the SQLite-side
127
+ BUSY where the user can retry the failing statement and continue
128
+ the same tx) AND RAFT-BUSY translated by the server's gateway
129
+ (the in-flight write was not accepted; the server-side tx state
130
+ is the same as engine-BUSY from the client's perspective).
131
+
132
+ The two shapes are indistinguishable at the Python layer per the
133
+ existing comment at ``dqliteclient/connection.py:2683-2698``
134
+ ("users must retry explicitly"). This retry IS that explicit
135
+ retry, applied uniformly across both BUSY origins.
136
+
137
+ Specifically NOT retried:
138
+
139
+ - ``DqliteConnectionError`` (the checkpoint-BUSY rewrap at
140
+ ``dqliteclient/connection.py:2717``) — bypasses this classifier
141
+ because it's not an ``OperationalError`` subclass. The
142
+ checkpoint-BUSY case already triggers SA pool invalidation by
143
+ design (the connection's tx state was reset; retry on the same
144
+ connection is wrong).
145
+ - Other ``OperationalError`` codes (LEADER_ERROR_CODES,
146
+ INTERFACE_ERROR_CODES, etc.) — leader-flip and transport
147
+ faults have their own retry semantics elsewhere; this gate
148
+ narrows to BUSY only.
149
+ """
150
+ if not isinstance(exc, _OperationalError):
151
+ return False
152
+ return getattr(exc, "code", None) == _SQLITE_BUSY
153
+
154
+
155
+ def retry_sync_on_busy[T](
156
+ busy_timeout: float,
157
+ run_sync: Callable[[Coroutine[Any, Any, T]], T],
158
+ coro_factory: Callable[[], Coroutine[Any, Any, T]],
159
+ ) -> T:
160
+ """Module-level sync retry loop.
161
+
162
+ Caller provides ``run_sync`` (the function that drives the
163
+ coroutine on the connection's daemon loop — typically
164
+ ``connection._run_sync``) and ``coro_factory`` (a callable that
165
+ returns a fresh coroutine each time the retry needs to re-submit).
166
+ Coroutines are single-use so the factory shape is mandatory.
167
+
168
+ Same retry shape as the inline-on-connection variants: SQLite-curve
169
+ sleeps, BUSY-only classification (``OperationalError(code=
170
+ SQLITE_BUSY)``), propagate non-BUSY exceptions unchanged.
171
+ ``KeyboardInterrupt`` / ``SystemExit`` during ``time.sleep``
172
+ propagate (those are ``BaseException``, not caught by ``except
173
+ OperationalError``).
174
+
175
+ ``busy_timeout <= 0`` short-circuits to a single ``run_sync``
176
+ call with no retry (stdlib parity for ``timeout=0``).
177
+
178
+ **Cross-thread caveat under** ``check_same_thread=False``:
179
+
180
+ The retry loop releases the connection's ``_op_lock`` between
181
+ attempts. ``run_sync`` acquires the lock at the START of each
182
+ attempt and releases it before returning; the ``time.sleep``
183
+ that follows runs OUTSIDE the lock so the sleep doesn't starve
184
+ sibling threads. By design — holding ``_op_lock`` across a
185
+ 100ms sleep would defeat cross-thread sharing.
186
+
187
+ On a Connection shared across threads via
188
+ ``check_same_thread=False``, a BUSY-retried statement may
189
+ observe interleaved writes from sibling threads between retry
190
+ attempts. Example:
191
+
192
+ Thread A: cur.execute("UPDATE t SET v=v+1 WHERE k=?") -> BUSY
193
+ Thread A: sleeps for SQLite-curve delay (op_lock released)
194
+ Thread B: cur.execute("UPDATE t SET v=v+1 WHERE k=?") -> succeeds
195
+ Thread A: retries -> sees B's update; may succeed against
196
+ a different row state than the original attempt.
197
+
198
+ This is the correct semantic for retry-on-contention (each
199
+ retry sees current state, the same as it would on a fresh
200
+ connection). Callers who need to retry against an atomic
201
+ snapshot must wrap in an explicit transaction:
202
+
203
+ with conn.transaction():
204
+ cur.execute("UPDATE t SET v=v+1 WHERE k=?", (k,))
205
+
206
+ The transaction holds the wire lock across the entire body, so
207
+ sibling threads cannot interleave statements between the retry
208
+ attempts inside that body.
209
+ """
210
+ busy_timeout_ms = int(busy_timeout * 1000)
211
+ if busy_timeout_ms <= 0:
212
+ return run_sync(coro_factory())
213
+ count = 0
214
+ while True:
215
+ try:
216
+ return run_sync(coro_factory())
217
+ except _OperationalError as exc:
218
+ if not is_busy_exception(exc):
219
+ raise
220
+ delay_ms = next_busy_delay_ms(count, busy_timeout_ms)
221
+ if delay_ms is None:
222
+ logger.warning(
223
+ "busy_timeout budget %.3fs exhausted after %d retries; raising original BUSY",
224
+ busy_timeout,
225
+ count,
226
+ )
227
+ raise
228
+ logger.debug(
229
+ "BUSY retry attempt %d: sleeping %dms (budget %.3fs)",
230
+ count,
231
+ delay_ms,
232
+ busy_timeout,
233
+ )
234
+ time.sleep(delay_ms / 1000.0)
235
+ count += 1
236
+
237
+
238
+ async def retry_async_on_busy[T](
239
+ busy_timeout: float,
240
+ coro_factory: Callable[[], Awaitable[T]],
241
+ ) -> T:
242
+ """Module-level async retry loop. Used by the per-iteration
243
+ executemany path that cannot route through
244
+ ``Connection._await_with_busy_retry`` because test fixtures
245
+ construct ``Cursor`` instances with a ``MagicMock`` connection
246
+ that does not have that method.
247
+
248
+ Same retry shape as the connection-method variants: SQLite-curve
249
+ sleeps, BUSY-only classification, propagate non-BUSY unchanged.
250
+ ``CancelledError`` propagates through ``await asyncio.sleep``
251
+ because the ``except OperationalError`` arm does not catch
252
+ ``BaseException``.
253
+
254
+ ``busy_timeout <= 0`` short-circuits to a single awaitable call
255
+ with no retry (stdlib parity).
256
+
257
+ **Cross-task caveat**:
258
+
259
+ Mirrors the sync sibling's cross-thread caveat: the retry loop
260
+ releases any wire-level lock (the inner ``DqliteConnection``'s
261
+ op_lock or the AsyncConnection's op_lock) between attempts.
262
+ ``await asyncio.sleep`` runs OUTSIDE the lock so siblings can
263
+ make progress; by design.
264
+
265
+ On an AsyncConnection shared across asyncio tasks on the same
266
+ loop, a BUSY-retried statement may observe interleaved writes
267
+ from sibling tasks between retry attempts. Same shape as the
268
+ sync sibling: each retry sees current state. Callers who need
269
+ to retry against an atomic snapshot must wrap in an explicit
270
+ ``async with conn.transaction():`` block.
271
+ """
272
+ busy_timeout_ms = int(busy_timeout * 1000)
273
+ if busy_timeout_ms <= 0:
274
+ return await coro_factory()
275
+ count = 0
276
+ while True:
277
+ try:
278
+ return await coro_factory()
279
+ except _OperationalError as exc:
280
+ if not is_busy_exception(exc):
281
+ raise
282
+ delay_ms = next_busy_delay_ms(count, busy_timeout_ms)
283
+ if delay_ms is None:
284
+ logger.warning(
285
+ "busy_timeout budget %.3fs exhausted after %d retries; raising original BUSY",
286
+ busy_timeout,
287
+ count,
288
+ )
289
+ raise
290
+ logger.debug(
291
+ "BUSY retry attempt %d: sleeping %dms (budget %.3fs)",
292
+ count,
293
+ delay_ms,
294
+ busy_timeout,
295
+ )
296
+ await asyncio.sleep(delay_ms / 1000.0)
297
+ count += 1