dqlite-dbapi 0.3.0__tar.gz → 0.4.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 (553) hide show
  1. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/DEVELOPMENT.md +2 -2
  2. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/PKG-INFO +6 -4
  3. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/README.md +2 -0
  4. dqlite_dbapi-0.4.0/docs/architecture.md +169 -0
  5. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/docs/differences-from-sqlite3.md +9 -0
  6. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/docs/transactions.md +24 -12
  7. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/pyproject.toml +3 -3
  8. dqlite_dbapi-0.4.0/src/dqlitedbapi/__init__.py +156 -0
  9. dqlite_dbapi-0.4.0/src/dqlitedbapi/_busy.py +57 -0
  10. dqlite_dbapi-0.4.0/src/dqlitedbapi/_loop.py +105 -0
  11. dqlite_dbapi-0.4.0/src/dqlitedbapi/_module.py +35 -0
  12. dqlite_dbapi-0.4.0/src/dqlitedbapi/_sql.py +204 -0
  13. dqlite_dbapi-0.4.0/src/dqlitedbapi/_stubs.py +103 -0
  14. dqlite_dbapi-0.4.0/src/dqlitedbapi/aio/__init__.py +204 -0
  15. dqlite_dbapi-0.4.0/src/dqlitedbapi/aio/connection.py +549 -0
  16. dqlite_dbapi-0.4.0/src/dqlitedbapi/aio/cursor.py +480 -0
  17. dqlite_dbapi-0.4.0/src/dqlitedbapi/connection.py +300 -0
  18. dqlite_dbapi-0.4.0/src/dqlitedbapi/cursor.py +151 -0
  19. dqlite_dbapi-0.4.0/src/dqlitedbapi/exceptions.py +282 -0
  20. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/src/dqlitedbapi/row.py +8 -22
  21. dqlite_dbapi-0.4.0/src/dqlitedbapi/types.py +364 -0
  22. dqlite_dbapi-0.4.0/tests/conftest.py +29 -0
  23. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_async_concurrent_executemany_serialized.py +1 -1
  24. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_async_executemany_param_count.py +1 -1
  25. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_datetime_conversion.py +8 -8
  26. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_executemany_cancel_mid_batch.py +4 -4
  27. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_executemany_completed_iterations_observability.py +3 -4
  28. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_executemany_failure_resets_rowcount.py +4 -4
  29. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_force_close_unblocks_run_sync.py +1 -1
  30. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_lastrowid_returning.py +1 -1
  31. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_leader_redirect_live.py +1 -1
  32. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_misc_coverage.py +4 -4
  33. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_pragma_deny_list.py +2 -2
  34. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_session_mode_read_only.py +3 -3
  35. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_async_cursor_aiter_aenter_pep249_pins.py +4 -4
  36. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_autocommit_doc_no_round_trip.py +3 -3
  37. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_autocommit_legacy_transaction_control.py +3 -3
  38. dqlite_dbapi-0.4.0/tests/test_commit_no_spurious_connect.py +18 -0
  39. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_conform_adapter_non_primitive_rejected_at_dbapi_layer.py +5 -5
  40. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_connect_isolation_level_autocommit_kwargs_symmetric.py +4 -4
  41. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_connection_eager_address_validation.py +5 -5
  42. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_convert_bind_param_conform_exception_propagates.py +5 -5
  43. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_cursor_after_external_close.py +4 -14
  44. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_date_widens_to_datetime.py +5 -5
  45. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_datetime_time_round_trip.py +6 -6
  46. dqlite_dbapi-0.4.0/tests/test_driver_rules.py +242 -0
  47. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_in_transaction_closed_divergence_doc.py +1 -1
  48. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_iso8601_aware_microseconds_round_trip.py +5 -5
  49. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_iso8601_decoder_leap_seconds_rejected.py +2 -2
  50. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_iso8601_decoder_subsecond_truncation.py +4 -4
  51. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_iso8601_encoder.py +68 -68
  52. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_iso8601_encoder_fold.py +7 -7
  53. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_iso8601_encoder_utcoffset_exception_wrap.py +7 -7
  54. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_iso8601_parse_error_truncation.py +6 -6
  55. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_iso8601_tzinfo_exception_classes_wrapped.py +3 -3
  56. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_isolation_level_stdlib_accept_set.py +6 -6
  57. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_legacy_transaction_control_constant.py +1 -1
  58. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_pep249_stub_hasattr_divergence.py +1 -1
  59. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_register_adapter_conform_fallback.py +9 -9
  60. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_repr.py +2 -3
  61. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_setinputsizes_accepts_none.py +5 -6
  62. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_setinputsizes_non_sequence_iterable.py +6 -8
  63. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_setoutputsize_accepts_none.py +5 -6
  64. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_text_bind_nul_byte_rejected_with_blob_hint.py +11 -11
  65. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_total_changes_hasattr_safe.py +1 -1
  66. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_type_coercion_documented_asymmetries.py +12 -12
  67. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_types_validator_branches.py +9 -9
  68. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_unregister_adapter_rejects_built_in_datetime_types.py +3 -3
  69. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_validate_ticks_and_unixtime_reject_bool_and_non_numeric.py +8 -8
  70. dqlite_dbapi-0.4.0/tests/test_with_stub_client.py +607 -0
  71. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/uv.lock +3 -3
  72. dqlite_dbapi-0.3.0/src/dqlitedbapi/__init__.py +0 -256
  73. dqlite_dbapi-0.3.0/src/dqlitedbapi/_busy_retry.py +0 -149
  74. dqlite_dbapi-0.3.0/src/dqlitedbapi/_constants.py +0 -37
  75. dqlite_dbapi-0.3.0/src/dqlitedbapi/_pragma_intercept.py +0 -130
  76. dqlite_dbapi-0.3.0/src/dqlitedbapi/aio/__init__.py +0 -373
  77. dqlite_dbapi-0.3.0/src/dqlitedbapi/aio/connection.py +0 -1697
  78. dqlite_dbapi-0.3.0/src/dqlitedbapi/aio/cursor.py +0 -970
  79. dqlite_dbapi-0.3.0/src/dqlitedbapi/connection.py +0 -2574
  80. dqlite_dbapi-0.3.0/src/dqlitedbapi/cursor.py +0 -1749
  81. dqlite_dbapi-0.3.0/src/dqlitedbapi/exceptions.py +0 -291
  82. dqlite_dbapi-0.3.0/src/dqlitedbapi/types.py +0 -590
  83. dqlite_dbapi-0.3.0/tests/aio/test_aio_version_final_annotated.py +0 -20
  84. dqlite_dbapi-0.3.0/tests/aio/test_async_close_underlying_close_failed_logged.py +0 -105
  85. dqlite_dbapi-0.3.0/tests/aio/test_async_connection_fork_guard.py +0 -115
  86. dqlite_dbapi-0.3.0/tests/aio/test_async_unclosed_warning_skips_in_fork_child.py +0 -143
  87. dqlite_dbapi-0.3.0/tests/aio/test_close_cancel_between_cursor_cascade_and_lock.py +0 -58
  88. dqlite_dbapi-0.3.0/tests/aio/test_fetchmany_cancel_atomic.py +0 -57
  89. dqlite_dbapi-0.3.0/tests/aio/test_messages_clear_on_closed.py +0 -49
  90. dqlite_dbapi-0.3.0/tests/conftest.py +0 -45
  91. dqlite_dbapi-0.3.0/tests/test_aconnect_cleanup_close_shielded_against_outer_timeout.py +0 -62
  92. dqlite_dbapi-0.3.0/tests/test_aconnect_cleanup_on_connect_failure.py +0 -75
  93. dqlite_dbapi-0.3.0/tests/test_adapter_exceptions_wrapped_as_data_error.py +0 -123
  94. dqlite_dbapi-0.3.0/tests/test_aenter_cleanup_nulls_locks_under_concurrent_close.py +0 -107
  95. dqlite_dbapi-0.3.0/tests/test_aenter_cleanup_preserves_original_under_outer_cancel.py +0 -61
  96. dqlite_dbapi-0.3.0/tests/test_aexit_behavioural_no_close.py +0 -64
  97. dqlite_dbapi-0.3.0/tests/test_aexit_commit_cancel_debug_log.py +0 -99
  98. dqlite_dbapi-0.3.0/tests/test_aexit_commit_cancel_force_closes_transport.py +0 -114
  99. dqlite_dbapi-0.3.0/tests/test_aexit_rollback_cancel_body_context_pin.py +0 -65
  100. dqlite_dbapi-0.3.0/tests/test_aexit_rollback_debug_log.py +0 -71
  101. dqlite_dbapi-0.3.0/tests/test_aio_cascade_close_clears_executing_task.py +0 -37
  102. dqlite_dbapi-0.3.0/tests/test_aio_commit_rollback_no_tx.py +0 -62
  103. dqlite_dbapi-0.3.0/tests/test_aio_commit_rollback_no_tx_code_matrix.py +0 -75
  104. dqlite_dbapi-0.3.0/tests/test_aio_commit_rollback_wrapping.py +0 -125
  105. dqlite_dbapi-0.3.0/tests/test_aio_connection_finalizer_detached_on_close.py +0 -44
  106. dqlite_dbapi-0.3.0/tests/test_aio_cursor_drain_rows.py +0 -69
  107. dqlite_dbapi-0.3.0/tests/test_aio_cursor_row_factory_typeerror_wrap.py +0 -80
  108. dqlite_dbapi-0.3.0/tests/test_aio_execute_unlocked_silent_drop_scrubs_state.py +0 -102
  109. dqlite_dbapi-0.3.0/tests/test_aio_executemany_cursor_close_cascade.py +0 -23
  110. dqlite_dbapi-0.3.0/tests/test_aio_force_close_foreign_thread_cursor_closed_pre_cascade.py +0 -91
  111. dqlite_dbapi-0.3.0/tests/test_aio_force_close_transport_cursor_cascade.py +0 -78
  112. dqlite_dbapi-0.3.0/tests/test_aio_init_reexports_diagnostic_prefixes.py +0 -31
  113. dqlite_dbapi-0.3.0/tests/test_aio_loop_affinity_exc_class.py +0 -54
  114. dqlite_dbapi-0.3.0/tests/test_aio_module_attributes.py +0 -79
  115. dqlite_dbapi-0.3.0/tests/test_aio_row_factory_setter_loop_binding.py +0 -76
  116. dqlite_dbapi-0.3.0/tests/test_aio_setters_clear_messages_pep249.py +0 -37
  117. dqlite_dbapi-0.3.0/tests/test_aio_transaction_recheck_after_ensure_connection.py +0 -79
  118. dqlite_dbapi-0.3.0/tests/test_arraysize_validation.py +0 -107
  119. dqlite_dbapi-0.3.0/tests/test_async_check_loop_only_pid_before_loop.py +0 -55
  120. dqlite_dbapi-0.3.0/tests/test_async_close_cancel_arm_cancels_inner_pending_drain.py +0 -101
  121. dqlite_dbapi-0.3.0/tests/test_async_close_ensure_locks_guard.py +0 -34
  122. dqlite_dbapi-0.3.0/tests/test_async_close_finally_propagates_cancel.py +0 -127
  123. dqlite_dbapi-0.3.0/tests/test_async_close_op_lock_bounded.py +0 -128
  124. dqlite_dbapi-0.3.0/tests/test_async_close_outer_cancel_force_closes_transport.py +0 -89
  125. dqlite_dbapi-0.3.0/tests/test_async_close_race.py +0 -106
  126. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_force_close_on_outer_cancel.py +0 -101
  127. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_in_transaction_under_lock.py +0 -77
  128. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_loop_binding_first.py +0 -69
  129. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_messages_under_lock.py +0 -69
  130. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_op_lock_bounded.py +0 -54
  131. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_phase_budget.py +0 -92
  132. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_retry_after_invalidate.py +0 -111
  133. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_timeout_outer_scope_hint.py +0 -105
  134. dqlite_dbapi-0.3.0/tests/test_async_commit_rollback_timeout_phase_aware.py +0 -100
  135. dqlite_dbapi-0.3.0/tests/test_async_connect_cross_loop_diagnostic_on_fast_path.py +0 -47
  136. dqlite_dbapi-0.3.0/tests/test_async_connection.py +0 -75
  137. dqlite_dbapi-0.3.0/tests/test_async_connection_close_during_connect.py +0 -51
  138. dqlite_dbapi-0.3.0/tests/test_async_connection_cursor_fork_guard.py +0 -63
  139. dqlite_dbapi-0.3.0/tests/test_async_connection_ensure_connection_post_yield_close_shielded.py +0 -139
  140. dqlite_dbapi-0.3.0/tests/test_async_connection_race.py +0 -40
  141. dqlite_dbapi-0.3.0/tests/test_async_connection_setters_loop_binding.py +0 -60
  142. dqlite_dbapi-0.3.0/tests/test_async_connection_transaction.py +0 -48
  143. dqlite_dbapi-0.3.0/tests/test_async_cursor.py +0 -248
  144. dqlite_dbapi-0.3.0/tests/test_async_cursor_add_races_cascade_clear.py +0 -94
  145. dqlite_dbapi-0.3.0/tests/test_async_cursor_aenter_cross_task_executing_guard.py +0 -57
  146. dqlite_dbapi-0.3.0/tests/test_async_cursor_aenter_gc_parent_pep249.py +0 -23
  147. dqlite_dbapi-0.3.0/tests/test_async_cursor_aiter_cross_loop_fail_fast.py +0 -50
  148. dqlite_dbapi-0.3.0/tests/test_async_cursor_anext_cooperative_yield.py +0 -116
  149. dqlite_dbapi-0.3.0/tests/test_async_cursor_arraysize_row_factory_loop_binding.py +0 -68
  150. dqlite_dbapi-0.3.0/tests/test_async_cursor_callproc_nextset_scroll_loop_binding.py +0 -78
  151. dqlite_dbapi-0.3.0/tests/test_async_cursor_close_clears_executing_task.py +0 -59
  152. dqlite_dbapi-0.3.0/tests/test_async_cursor_close_is_sync_and_race_safe.py +0 -90
  153. dqlite_dbapi-0.3.0/tests/test_async_cursor_close_lock_free.py +0 -52
  154. dqlite_dbapi-0.3.0/tests/test_async_cursor_closed_state_pep249.py +0 -92
  155. dqlite_dbapi-0.3.0/tests/test_async_cursor_execute_executing_task_inside_try_frame.py +0 -47
  156. dqlite_dbapi-0.3.0/tests/test_async_cursor_execute_messages_clear.py +0 -59
  157. dqlite_dbapi-0.3.0/tests/test_async_cursor_executemany_validation_clears_executing_task.py +0 -94
  158. dqlite_dbapi-0.3.0/tests/test_async_cursor_fetch_loop_binding.py +0 -125
  159. dqlite_dbapi-0.3.0/tests/test_async_cursor_finally_clear_executing_task_unconditional.py +0 -41
  160. dqlite_dbapi-0.3.0/tests/test_async_cursor_late_add_proxy_swap.py +0 -34
  161. dqlite_dbapi-0.3.0/tests/test_async_cursor_loop_binding.py +0 -47
  162. dqlite_dbapi-0.3.0/tests/test_async_cursor_repr_with_gced_parent_connection.py +0 -29
  163. dqlite_dbapi-0.3.0/tests/test_async_cursor_rownumber_closed_cross_loop.py +0 -55
  164. dqlite_dbapi-0.3.0/tests/test_async_cursor_setinputsizes_loop_binding.py +0 -46
  165. dqlite_dbapi-0.3.0/tests/test_async_executemany_atomic.py +0 -58
  166. dqlite_dbapi-0.3.0/tests/test_async_executemany_close_race.py +0 -56
  167. dqlite_dbapi-0.3.0/tests/test_async_fetch_does_not_lazy_bind_loop.py +0 -41
  168. dqlite_dbapi-0.3.0/tests/test_async_fetchall_row_factory_cooperative_yield.py +0 -166
  169. dqlite_dbapi-0.3.0/tests/test_async_fetchall_row_factory_happy_path.py +0 -46
  170. dqlite_dbapi-0.3.0/tests/test_async_fetchmany_row_factory_cooperative_yield.py +0 -153
  171. dqlite_dbapi-0.3.0/tests/test_async_force_close_transport.py +0 -153
  172. dqlite_dbapi-0.3.0/tests/test_async_in_transaction_closed_foreign_loop.py +0 -58
  173. dqlite_dbapi-0.3.0/tests/test_async_in_transaction_loop_binding.py +0 -61
  174. dqlite_dbapi-0.3.0/tests/test_async_invalidated_property.py +0 -48
  175. dqlite_dbapi-0.3.0/tests/test_async_lock_binding.py +0 -111
  176. dqlite_dbapi-0.3.0/tests/test_async_resource_warning_gate.py +0 -109
  177. dqlite_dbapi-0.3.0/tests/test_async_resource_warning_sanitize_parity.py +0 -47
  178. dqlite_dbapi-0.3.0/tests/test_async_secondary_methods_clear_messages_before_guards.py +0 -117
  179. dqlite_dbapi-0.3.0/tests/test_async_transaction_owner_set_inside_try_frame.py +0 -78
  180. dqlite_dbapi-0.3.0/tests/test_async_unclosed_warning_shutdown_safety.py +0 -135
  181. dqlite_dbapi-0.3.0/tests/test_audit_2026_05_coverage_gaps.py +0 -256
  182. dqlite_dbapi-0.3.0/tests/test_audit_2026_05_dbapi_coverage.py +0 -332
  183. dqlite_dbapi-0.3.0/tests/test_autocommit_property.py +0 -64
  184. dqlite_dbapi-0.3.0/tests/test_autocommit_setter_exact_int_gate.py +0 -101
  185. dqlite_dbapi-0.3.0/tests/test_begin_rewrite_session_mode.py +0 -179
  186. dqlite_dbapi-0.3.0/tests/test_bind_typeerror_as_dataerror.py +0 -36
  187. dqlite_dbapi-0.3.0/tests/test_build_and_connect_base_exception_group_wrap.py +0 -105
  188. dqlite_dbapi-0.3.0/tests/test_build_and_connect_pragma_shield_observer.py +0 -58
  189. dqlite_dbapi-0.3.0/tests/test_build_and_connect_resolve_leader_oserror_wrap.py +0 -57
  190. dqlite_dbapi-0.3.0/tests/test_build_and_connect_wire_encode_error_translation.py +0 -76
  191. dqlite_dbapi-0.3.0/tests/test_busy_retry_async_wiring.py +0 -138
  192. dqlite_dbapi-0.3.0/tests/test_busy_retry_classifier.py +0 -69
  193. dqlite_dbapi-0.3.0/tests/test_busy_retry_curve.py +0 -81
  194. dqlite_dbapi-0.3.0/tests/test_busy_retry_sync_wiring.py +0 -129
  195. dqlite_dbapi-0.3.0/tests/test_call_client_base_exception_group_wrap.py +0 -71
  196. dqlite_dbapi-0.3.0/tests/test_call_client_baseexceptiongroup_cancel_split.py +0 -80
  197. dqlite_dbapi-0.3.0/tests/test_call_client_mapping.py +0 -105
  198. dqlite_dbapi-0.3.0/tests/test_call_client_typeerror_message_does_not_blame_bind.py +0 -32
  199. dqlite_dbapi-0.3.0/tests/test_call_client_wire_encode_error.py +0 -30
  200. dqlite_dbapi-0.3.0/tests/test_callproc_scroll_clear_messages.py +0 -78
  201. dqlite_dbapi-0.3.0/tests/test_cascade_cursors_clears_messages.py +0 -79
  202. dqlite_dbapi-0.3.0/tests/test_check_same_thread_kwarg.py +0 -180
  203. dqlite_dbapi-0.3.0/tests/test_classify_caller_sql.py +0 -84
  204. dqlite_dbapi-0.3.0/tests/test_classify_caller_sql_structural_reject_precedence.py +0 -70
  205. dqlite_dbapi-0.3.0/tests/test_classify_operational_code_zero.py +0 -20
  206. dqlite_dbapi-0.3.0/tests/test_cleanup_loop_thread_disarms_inner_and_reaps_drain.py +0 -296
  207. dqlite_dbapi-0.3.0/tests/test_cleanup_loop_thread_finalizer_fork_safe.py +0 -150
  208. dqlite_dbapi-0.3.0/tests/test_cleanup_loop_thread_foreign_loop_short_floor.py +0 -160
  209. dqlite_dbapi-0.3.0/tests/test_close_bypass_path_releases_latched_op_lock.py +0 -59
  210. dqlite_dbapi-0.3.0/tests/test_close_resource_leak.py +0 -31
  211. dqlite_dbapi-0.3.0/tests/test_close_signal_handler_reentry.py +0 -125
  212. dqlite_dbapi-0.3.0/tests/test_close_surfaces_op_lock_contention.py +0 -52
  213. dqlite_dbapi-0.3.0/tests/test_close_thread_join_uses_close_timeout.py +0 -157
  214. dqlite_dbapi-0.3.0/tests/test_close_timeout_floor_enforcement.py +0 -47
  215. dqlite_dbapi-0.3.0/tests/test_close_timeout_plumbing.py +0 -101
  216. dqlite_dbapi-0.3.0/tests/test_closed_property.py +0 -96
  217. dqlite_dbapi-0.3.0/tests/test_cluster_policy_rejection_prefix_constant.py +0 -53
  218. dqlite_dbapi-0.3.0/tests/test_code_to_exception_wire_ssot.py +0 -35
  219. dqlite_dbapi-0.3.0/tests/test_collated_coverage_gaps.py +0 -151
  220. dqlite_dbapi-0.3.0/tests/test_commit_after_external_close.py +0 -67
  221. dqlite_dbapi-0.3.0/tests/test_commit_leader_flip_ambiguous_commit_error.py +0 -241
  222. dqlite_dbapi-0.3.0/tests/test_commit_leader_flip_propagates.py +0 -114
  223. dqlite_dbapi-0.3.0/tests/test_commit_no_spurious_connect.py +0 -27
  224. dqlite_dbapi-0.3.0/tests/test_commit_rollback_shortcircuit_uses_in_transaction.py +0 -130
  225. dqlite_dbapi-0.3.0/tests/test_connect_baseexceptiongroup_cancel_split.py +0 -86
  226. dqlite_dbapi-0.3.0/tests/test_connect_busy_timeout_kwarg.py +0 -86
  227. dqlite_dbapi-0.3.0/tests/test_connect_clears_messages.py +0 -41
  228. dqlite_dbapi-0.3.0/tests/test_connect_iso_autocommit_round_trip.py +0 -97
  229. dqlite_dbapi-0.3.0/tests/test_connect_preserves_code.py +0 -69
  230. dqlite_dbapi-0.3.0/tests/test_connection.py +0 -38
  231. dqlite_dbapi-0.3.0/tests/test_connection_after_fork_raises.py +0 -125
  232. dqlite_dbapi-0.3.0/tests/test_connection_autocommit_isolation_level_closed_state.py +0 -66
  233. dqlite_dbapi-0.3.0/tests/test_connection_close_cascades_cursors.py +0 -67
  234. dqlite_dbapi-0.3.0/tests/test_connection_close_cursor_cascade_atomicity.py +0 -62
  235. dqlite_dbapi-0.3.0/tests/test_connection_close_idempotent_cross_thread.py +0 -55
  236. dqlite_dbapi-0.3.0/tests/test_connection_closed_checks.py +0 -205
  237. dqlite_dbapi-0.3.0/tests/test_connection_cursor_closed_state_precedes_unknown_kwarg.py +0 -35
  238. dqlite_dbapi-0.3.0/tests/test_connection_database_empty_string_rejected.py +0 -45
  239. dqlite_dbapi-0.3.0/tests/test_connection_database_kwarg_isinstance.py +0 -38
  240. dqlite_dbapi-0.3.0/tests/test_connection_enter_eager.py +0 -30
  241. dqlite_dbapi-0.3.0/tests/test_connection_exit_commit_ki_breadcrumb.py +0 -79
  242. dqlite_dbapi-0.3.0/tests/test_connection_exit_short_circuits_on_closed.py +0 -70
  243. dqlite_dbapi-0.3.0/tests/test_connection_messages_cleared_by_cursor_methods.py +0 -53
  244. dqlite_dbapi-0.3.0/tests/test_connection_row_factory_setter_closed_check.py +0 -50
  245. dqlite_dbapi-0.3.0/tests/test_context_manager_exit.py +0 -79
  246. dqlite_dbapi-0.3.0/tests/test_convert_bind_param_unsupported_vs_bad_adapter_message.py +0 -37
  247. dqlite_dbapi-0.3.0/tests/test_convert_params_async_cooperative_yield.py +0 -121
  248. dqlite_dbapi-0.3.0/tests/test_convert_params_conform_raise_propagates_unwrapped.py +0 -51
  249. dqlite_dbapi-0.3.0/tests/test_convert_rows_async_cooperative_yield.py +0 -106
  250. dqlite_dbapi-0.3.0/tests/test_convert_rows_async_probe_does_not_walk_all_row_types_up_front.py +0 -85
  251. dqlite_dbapi-0.3.0/tests/test_convert_rows_converter_free_fast_path.py +0 -109
  252. dqlite_dbapi-0.3.0/tests/test_cte_prefix_stripper_edges.py +0 -71
  253. dqlite_dbapi-0.3.0/tests/test_cursor.py +0 -215
  254. dqlite_dbapi-0.3.0/tests/test_cursor_arraysize_row_factory_setter_thread.py +0 -115
  255. dqlite_dbapi-0.3.0/tests/test_cursor_close_clears_messages.py +0 -32
  256. dqlite_dbapi-0.3.0/tests/test_cursor_close_cross_thread.py +0 -43
  257. dqlite_dbapi-0.3.0/tests/test_cursor_close_releases_connection_pin.py +0 -46
  258. dqlite_dbapi-0.3.0/tests/test_cursor_closed_state_pep249.py +0 -74
  259. dqlite_dbapi-0.3.0/tests/test_cursor_completed_iterations_resets_on_single_execute.py +0 -73
  260. dqlite_dbapi-0.3.0/tests/test_cursor_connection_partial_init_attributeerror.py +0 -42
  261. dqlite_dbapi-0.3.0/tests/test_cursor_connection_property_after_gc_pep249.py +0 -45
  262. dqlite_dbapi-0.3.0/tests/test_cursor_execute_state_reset_on_failure.py +0 -366
  263. dqlite_dbapi-0.3.0/tests/test_cursor_executemany_outer_shape_check.py +0 -72
  264. dqlite_dbapi-0.3.0/tests/test_cursor_force_close_race.py +0 -66
  265. dqlite_dbapi-0.3.0/tests/test_cursor_iter_clears_messages_pep249.py +0 -71
  266. dqlite_dbapi-0.3.0/tests/test_cursor_iterator_reset.py +0 -91
  267. dqlite_dbapi-0.3.0/tests/test_cursor_kwargs_rejection_precedence.py +0 -61
  268. dqlite_dbapi-0.3.0/tests/test_cursor_next_row_factory_raise_replays_row.py +0 -104
  269. dqlite_dbapi-0.3.0/tests/test_cursor_per_row_types.py +0 -153
  270. dqlite_dbapi-0.3.0/tests/test_cursor_post_close_property_reads.py +0 -100
  271. dqlite_dbapi-0.3.0/tests/test_cursor_protocol_check.py +0 -50
  272. dqlite_dbapi-0.3.0/tests/test_cursor_rejects_generator_params.py +0 -38
  273. dqlite_dbapi-0.3.0/tests/test_cursor_repr_with_gced_parent_connection.py +0 -53
  274. dqlite_dbapi-0.3.0/tests/test_cursor_row_factory_rejecting_setter.py +0 -97
  275. dqlite_dbapi-0.3.0/tests/test_cursor_row_factory_subclass_inheritance.py +0 -68
  276. dqlite_dbapi-0.3.0/tests/test_cursor_row_index_reset.py +0 -76
  277. dqlite_dbapi-0.3.0/tests/test_cursor_rowcount_after_ddl_stdlib_parity.py +0 -195
  278. dqlite_dbapi-0.3.0/tests/test_cursor_rownumber_affinity.py +0 -54
  279. dqlite_dbapi-0.3.0/tests/test_cursor_rownumber_post_close.py +0 -64
  280. dqlite_dbapi-0.3.0/tests/test_cursor_scroll_mode_validation.py +0 -126
  281. dqlite_dbapi-0.3.0/tests/test_cursor_secondary_methods_positional_only.py +0 -116
  282. dqlite_dbapi-0.3.0/tests/test_cursor_setinputsizes_affinity_precedes_shape.py +0 -151
  283. dqlite_dbapi-0.3.0/tests/test_cursor_setinputsizes_setoutputsize_closed_state_precedes_validation.py +0 -90
  284. dqlite_dbapi-0.3.0/tests/test_cursor_setters_clear_messages_pep249.py +0 -48
  285. dqlite_dbapi-0.3.0/tests/test_cursor_stop_iteration_contract.py +0 -82
  286. dqlite_dbapi-0.3.0/tests/test_cursors_weakset_state_lock.py +0 -104
  287. dqlite_dbapi-0.3.0/tests/test_dbapi_commit_deferred_fk.py +0 -31
  288. dqlite_dbapi-0.3.0/tests/test_dbapi_fork_guard_cluster.py +0 -204
  289. dqlite_dbapi-0.3.0/tests/test_dbapi_hardening.py +0 -548
  290. dqlite_dbapi-0.3.0/tests/test_description_null_first_row_resolves.py +0 -136
  291. dqlite_dbapi-0.3.0/tests/test_description_null_rescue_scan_yields_cooperatively.py +0 -144
  292. dqlite_dbapi-0.3.0/tests/test_description_null_type_code_to_none.py +0 -90
  293. dqlite_dbapi-0.3.0/tests/test_description_strict_type_codes.py +0 -53
  294. dqlite_dbapi-0.3.0/tests/test_description_type_code_unknown_sentinel.py +0 -42
  295. dqlite_dbapi-0.3.0/tests/test_diagnostic_messages.py +0 -131
  296. dqlite_dbapi-0.3.0/tests/test_dial_attempt_timeout_propagation.py +0 -96
  297. dqlite_dbapi-0.3.0/tests/test_dial_func_propagation.py +0 -67
  298. dqlite_dbapi-0.3.0/tests/test_dial_func_propagation_to_leader_resolve.py +0 -152
  299. dqlite_dbapi-0.3.0/tests/test_dqlite_namespace_error_classification.py +0 -37
  300. dqlite_dbapi-0.3.0/tests/test_ensure_loop_fast_path_snapshot_dcl_defence.py +0 -79
  301. dqlite_dbapi-0.3.0/tests/test_ensure_loop_post_fork_pid_guard.py +0 -108
  302. dqlite_dbapi-0.3.0/tests/test_ensure_loop_signal_teardown.py +0 -125
  303. dqlite_dbapi-0.3.0/tests/test_error_code_mapping.py +0 -91
  304. dqlite_dbapi-0.3.0/tests/test_exception_coded_mixin.py +0 -90
  305. dqlite_dbapi-0.3.0/tests/test_exception_mapping.py +0 -65
  306. dqlite_dbapi-0.3.0/tests/test_exception_wrapping.py +0 -102
  307. dqlite_dbapi-0.3.0/tests/test_execute_caller_param_shape_preserves_prior_state.py +0 -89
  308. dqlite_dbapi-0.3.0/tests/test_execute_multi_statement_rejection.py +0 -87
  309. dqlite_dbapi-0.3.0/tests/test_execute_reject_scrubs_prior_result_state.py +0 -156
  310. dqlite_dbapi-0.3.0/tests/test_executemany_accumulator_semantics.py +0 -87
  311. dqlite_dbapi-0.3.0/tests/test_executemany_basecaught_state_reset.py +0 -172
  312. dqlite_dbapi-0.3.0/tests/test_executemany_cancel_restores_pre_batch_lastrowid.py +0 -132
  313. dqlite_dbapi-0.3.0/tests/test_executemany_cancellederror_restores_pre_batch_lastrowid.py +0 -101
  314. dqlite_dbapi-0.3.0/tests/test_executemany_classifier_resets_state_first.py +0 -89
  315. dqlite_dbapi-0.3.0/tests/test_executemany_completed_iterations_snapshot_conditional_restore.py +0 -128
  316. dqlite_dbapi-0.3.0/tests/test_executemany_dict_subclass_accepted.py +0 -64
  317. dqlite_dbapi-0.3.0/tests/test_executemany_empty.py +0 -92
  318. dqlite_dbapi-0.3.0/tests/test_executemany_empty_batch_preserves_lastrowid.py +0 -52
  319. dqlite_dbapi-0.3.0/tests/test_executemany_lastrowid_restored_pre_batch_on_raise.py +0 -145
  320. dqlite_dbapi-0.3.0/tests/test_executemany_none_seq_rejected.py +0 -59
  321. dqlite_dbapi-0.3.0/tests/test_executemany_per_iter_structural_rejects.py +0 -77
  322. dqlite_dbapi-0.3.0/tests/test_executemany_pragma_with_leading_comment.py +0 -62
  323. dqlite_dbapi-0.3.0/tests/test_executemany_push_skips_cascade_zeroed_cursor.py +0 -64
  324. dqlite_dbapi-0.3.0/tests/test_executemany_reject_scrubs_prior_result_state.py +0 -231
  325. dqlite_dbapi-0.3.0/tests/test_executemany_rejects_pure_queries.py +0 -93
  326. dqlite_dbapi-0.3.0/tests/test_executemany_rejects_transaction_verbs.py +0 -235
  327. dqlite_dbapi-0.3.0/tests/test_executemany_returning_cap.py +0 -61
  328. dqlite_dbapi-0.3.0/tests/test_exit_logs_rollback_failure.py +0 -76
  329. dqlite_dbapi-0.3.0/tests/test_fetchall_row_factory_raise_replay.py +0 -125
  330. dqlite_dbapi-0.3.0/tests/test_fetchmany_edges.py +0 -63
  331. dqlite_dbapi-0.3.0/tests/test_fetchmany_negative_size_rejected.py +0 -78
  332. dqlite_dbapi-0.3.0/tests/test_fetchmany_non_int_validation.py +0 -66
  333. dqlite_dbapi-0.3.0/tests/test_fetchmany_one_clear_per_call_pep249.py +0 -42
  334. dqlite_dbapi-0.3.0/tests/test_fetchmany_row_factory_raise_replay.py +0 -111
  335. dqlite_dbapi-0.3.0/tests/test_finalizer_strict_warnings.py +0 -38
  336. dqlite_dbapi-0.3.0/tests/test_force_close_cancel_and_observe_narrow_suppress.py +0 -19
  337. dqlite_dbapi-0.3.0/tests/test_force_close_transport_cleanup_tail_unconditional.py +0 -88
  338. dqlite_dbapi-0.3.0/tests/test_force_close_transport_clears_messages.py +0 -87
  339. dqlite_dbapi-0.3.0/tests/test_force_close_transport_cross_thread_cancel_threadsafe.py +0 -101
  340. dqlite_dbapi-0.3.0/tests/test_force_close_transport_cursor_cascade_threadsafe.py +0 -135
  341. dqlite_dbapi-0.3.0/tests/test_force_close_transport_defensive_arms.py +0 -83
  342. dqlite_dbapi-0.3.0/tests/test_force_close_transport_disarms_inner_finalizer.py +0 -68
  343. dqlite_dbapi-0.3.0/tests/test_force_close_transport_nulls_lock_and_loop_slots.py +0 -74
  344. dqlite_dbapi-0.3.0/tests/test_force_close_transport_pending_drain_resnapshot.py +0 -115
  345. dqlite_dbapi-0.3.0/tests/test_force_close_transport_writer_cross_thread.py +0 -114
  346. dqlite_dbapi-0.3.0/tests/test_fork_diagnostic_includes_pids.py +0 -41
  347. dqlite_dbapi-0.3.0/tests/test_format_utc_offset_non_timedelta_guard.py +0 -50
  348. dqlite_dbapi-0.3.0/tests/test_gc_cleanup.py +0 -43
  349. dqlite_dbapi-0.3.0/tests/test_get_async_connection_force_close_race.py +0 -101
  350. dqlite_dbapi-0.3.0/tests/test_get_resolve_leader_cluster_no_loop_raises_runtime_error.py +0 -24
  351. dqlite_dbapi-0.3.0/tests/test_in_transaction_after_close.py +0 -95
  352. dqlite_dbapi-0.3.0/tests/test_in_transaction_closed_foreign_thread.py +0 -85
  353. dqlite_dbapi-0.3.0/tests/test_in_transaction_property.py +0 -35
  354. dqlite_dbapi-0.3.0/tests/test_in_transaction_property_under_check_same_thread.py +0 -101
  355. dqlite_dbapi-0.3.0/tests/test_is_multi_statement_walker.py +0 -40
  356. dqlite_dbapi-0.3.0/tests/test_is_no_transaction_error.py +0 -113
  357. dqlite_dbapi-0.3.0/tests/test_is_row_returning_multiline_whitespace.py +0 -88
  358. dqlite_dbapi-0.3.0/tests/test_is_row_returning_sql_noise.py +0 -70
  359. dqlite_dbapi-0.3.0/tests/test_iterdump_stub_signature_3_13_filter_kwarg.py +0 -52
  360. dqlite_dbapi-0.3.0/tests/test_join_budget_helper_shutdown_safety.py +0 -70
  361. dqlite_dbapi-0.3.0/tests/test_lastrowid_insert_only.py +0 -68
  362. dqlite_dbapi-0.3.0/tests/test_lastrowid_signed_int64_cast.py +0 -51
  363. dqlite_dbapi-0.3.0/tests/test_leader_redirect_on_connect.py +0 -207
  364. dqlite_dbapi-0.3.0/tests/test_max_continuation_frames_upper_bound.py +0 -53
  365. dqlite_dbapi-0.3.0/tests/test_max_message_size_round_trip.py +0 -51
  366. dqlite_dbapi-0.3.0/tests/test_max_total_rows_plumbing.py +0 -63
  367. dqlite_dbapi-0.3.0/tests/test_max_total_rows_validation.py +0 -47
  368. dqlite_dbapi-0.3.0/tests/test_messages_attribute.py +0 -33
  369. dqlite_dbapi-0.3.0/tests/test_messages_clear_on_ctxmgr_entry.py +0 -67
  370. dqlite_dbapi-0.3.0/tests/test_module_attributes.py +0 -223
  371. dqlite_dbapi-0.3.0/tests/test_module_level_stubs.py +0 -88
  372. dqlite_dbapi-0.3.0/tests/test_multi_statement_classifier_allows_trigger_bodies.py +0 -66
  373. dqlite_dbapi-0.3.0/tests/test_no_transaction_substrings_source_of_truth.py +0 -22
  374. dqlite_dbapi-0.3.0/tests/test_no_tx_primary_codes_invariant.py +0 -41
  375. dqlite_dbapi-0.3.0/tests/test_op_lock_bounded_acquire.py +0 -69
  376. dqlite_dbapi-0.3.0/tests/test_operational_error_classification.py +0 -64
  377. dqlite_dbapi-0.3.0/tests/test_operational_error_raw_message.py +0 -62
  378. dqlite_dbapi-0.3.0/tests/test_param_conversion.py +0 -59
  379. dqlite_dbapi-0.3.0/tests/test_param_validation.py +0 -42
  380. dqlite_dbapi-0.3.0/tests/test_pep249_database_error_routing.py +0 -41
  381. dqlite_dbapi-0.3.0/tests/test_pep249_messages_clearing.py +0 -233
  382. dqlite_dbapi-0.3.0/tests/test_pep249_messages_independence.py +0 -73
  383. dqlite_dbapi-0.3.0/tests/test_pep249_misc_pins.py +0 -124
  384. dqlite_dbapi-0.3.0/tests/test_pragma_busy_timeout_intercept.py +0 -208
  385. dqlite_dbapi-0.3.0/tests/test_pragma_write_description_none.py +0 -48
  386. dqlite_dbapi-0.3.0/tests/test_property_round_trip_and_fork_guard.py +0 -223
  387. dqlite_dbapi-0.3.0/tests/test_property_setters_do_not_mirror_to_inner_async_conn.py +0 -44
  388. dqlite_dbapi-0.3.0/tests/test_protocol_serialization.py +0 -79
  389. dqlite_dbapi-0.3.0/tests/test_repr_sanitizes_address.py +0 -131
  390. dqlite_dbapi-0.3.0/tests/test_resolve_leader_cache_dial_func_id_recycle.py +0 -130
  391. dqlite_dbapi-0.3.0/tests/test_resolve_leader_cache_lock_atfork_hook.py +0 -25
  392. dqlite_dbapi-0.3.0/tests/test_resolve_leader_cluster_cache.py +0 -93
  393. dqlite_dbapi-0.3.0/tests/test_resolve_leader_cluster_constructs_outside_lock.py +0 -87
  394. dqlite_dbapi-0.3.0/tests/test_resolve_leader_cross_loop_isolation.py +0 -187
  395. dqlite_dbapi-0.3.0/tests/test_rollback_busy_retry.py +0 -81
  396. dqlite_dbapi-0.3.0/tests/test_row_factory_sqlite3_row_surfaces_dataerror.py +0 -53
  397. dqlite_dbapi-0.3.0/tests/test_rownumber_closed_cursor_returns_none_documented.py +0 -60
  398. dqlite_dbapi-0.3.0/tests/test_run_sync_ensure_loop_raise_closes_coroutine.py +0 -59
  399. dqlite_dbapi-0.3.0/tests/test_run_sync_finally_suppresses_double_release.py +0 -83
  400. dqlite_dbapi-0.3.0/tests/test_run_sync_keyboard_interrupt.py +0 -287
  401. dqlite_dbapi-0.3.0/tests/test_run_sync_keyboard_interrupt_cleanup_paths.py +0 -133
  402. dqlite_dbapi-0.3.0/tests/test_run_sync_ki_post_schedule_pre_await.py +0 -71
  403. dqlite_dbapi-0.3.0/tests/test_run_sync_ki_race_recovery.py +0 -164
  404. dqlite_dbapi-0.3.0/tests/test_run_sync_loop_closed_during_schedule.py +0 -93
  405. dqlite_dbapi-0.3.0/tests/test_run_sync_other_runtimeerror_propagates.py +0 -88
  406. dqlite_dbapi-0.3.0/tests/test_run_sync_owner_stamp_window.py +0 -82
  407. dqlite_dbapi-0.3.0/tests/test_run_sync_timeout.py +0 -352
  408. dqlite_dbapi-0.3.0/tests/test_run_sync_timeout_recovery_preserves_conn.py +0 -85
  409. dqlite_dbapi-0.3.0/tests/test_secondary_methods_clear_messages_before_thread_check.py +0 -75
  410. dqlite_dbapi-0.3.0/tests/test_setinputsizes_clears_messages_per_pep249.py +0 -45
  411. dqlite_dbapi-0.3.0/tests/test_setinputsizes_short_circuit_on_closed_connection.py +0 -83
  412. dqlite_dbapi-0.3.0/tests/test_setter_closed_state_precedes_thread.py +0 -63
  413. dqlite_dbapi-0.3.0/tests/test_strip_leading_comments_bom.py +0 -52
  414. dqlite_dbapi-0.3.0/tests/test_strip_leading_comments_parity.py +0 -62
  415. dqlite_dbapi-0.3.0/tests/test_strip_sql_noise_backtick_identifier.py +0 -43
  416. dqlite_dbapi-0.3.0/tests/test_submodule_all.py +0 -32
  417. dqlite_dbapi-0.3.0/tests/test_sync_close_op_lock_owner_aware_probe.py +0 -78
  418. dqlite_dbapi-0.3.0/tests/test_sync_commit_rollback_closed_precedes_thread.py +0 -38
  419. dqlite_dbapi-0.3.0/tests/test_sync_commit_rollback_inside_ctxmgr_guard.py +0 -87
  420. dqlite_dbapi-0.3.0/tests/test_sync_commit_rollback_messages_under_lock.py +0 -64
  421. dqlite_dbapi-0.3.0/tests/test_sync_commit_rollback_protocol_recheck.py +0 -37
  422. dqlite_dbapi-0.3.0/tests/test_sync_commit_rollback_retry_after_invalidate.py +0 -68
  423. dqlite_dbapi-0.3.0/tests/test_sync_cursor_post_add_closed_recheck.py +0 -102
  424. dqlite_dbapi-0.3.0/tests/test_sync_cursor_rownumber_closed_cross_thread.py +0 -50
  425. dqlite_dbapi-0.3.0/tests/test_sync_execute_async_pre_wire_close_recheck.py +0 -65
  426. dqlite_dbapi-0.3.0/tests/test_sync_execute_async_silent_drop_scrubs_state.py +0 -94
  427. dqlite_dbapi-0.3.0/tests/test_sync_executemany_close_race.py +0 -56
  428. dqlite_dbapi-0.3.0/tests/test_sync_exit_signal_mid_rollback.py +0 -66
  429. dqlite_dbapi-0.3.0/tests/test_sync_fetchmany_cancel_atomic.py +0 -60
  430. dqlite_dbapi-0.3.0/tests/test_sync_force_close_transport.py +0 -147
  431. dqlite_dbapi-0.3.0/tests/test_sync_force_close_transport_pending_drain_reap.py +0 -145
  432. dqlite_dbapi-0.3.0/tests/test_sync_invalidated_property.py +0 -47
  433. dqlite_dbapi-0.3.0/tests/test_sync_run_sync_multiphase_timeout.py +0 -87
  434. dqlite_dbapi-0.3.0/tests/test_sync_transaction_ctxmgr_owner_slot_busy_sentinel.py +0 -14
  435. dqlite_dbapi-0.3.0/tests/test_sync_transaction_owner_token_bookkeeping.py +0 -110
  436. dqlite_dbapi-0.3.0/tests/test_sync_transaction_signal_in_restore_reaps_owner.py +0 -67
  437. dqlite_dbapi-0.3.0/tests/test_thread_safety_enforcement.py +0 -327
  438. dqlite_dbapi-0.3.0/tests/test_transaction_ctxmgr_state_lock.py +0 -129
  439. dqlite_dbapi-0.3.0/tests/test_unsupported_method_stubs.py +0 -352
  440. dqlite_dbapi-0.3.0/tests/test_validate_positive_int_pep249_wrap.py +0 -87
  441. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/.github/workflows/publish-to-pypi.yml +0 -0
  442. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/.gitignore +0 -0
  443. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/LICENSE.md +0 -0
  444. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/docs/differences-from-aiosqlite.md +0 -0
  445. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/src/dqlitedbapi/py.typed +0 -0
  446. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/__init__.py +0 -0
  447. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/aio/test_async_iter_protocol_invariants.py +0 -0
  448. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/__init__.py +0 -0
  449. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/conftest.py +0 -0
  450. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_arraysize_zero_fetchmany.py +0 -0
  451. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_async_cursor_concurrent_execute_rejected.py +0 -0
  452. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_async_cursor_edge_branches.py +0 -0
  453. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_async_fetchmany_cancel.py +0 -0
  454. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_async_returning_zero_rows.py +0 -0
  455. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_async_transaction_ctxmgr_rejects_stray_commit.py +0 -0
  456. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_boolean_conversion.py +0 -0
  457. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_close_implicit_rollback_pep249.py +0 -0
  458. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_concurrent_cursors_visibility.py +0 -0
  459. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_constraint_extended_codes.py +0 -0
  460. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_context_manager.py +0 -0
  461. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_cte_prefixed_dml_metadata.py +0 -0
  462. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_cursor_fetch_after_non_row_execute.py +0 -0
  463. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_cursor_misc_invariant_pins.py +0 -0
  464. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_cursor_row_index_after_fetchall.py +0 -0
  465. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_description_after_ddl_pragma.py +0 -0
  466. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_description_survives_empty_fetchall.py +0 -0
  467. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_executemany_cte_dml.py +0 -0
  468. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_executemany_generator.py +0 -0
  469. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_executemany_preserves_lastrowid.py +0 -0
  470. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_executemany_returning.py +0 -0
  471. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_integrity_error.py +0 -0
  472. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_lastrowid_autoincrement.py +0 -0
  473. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_lastrowid_cursor_scoped.py +0 -0
  474. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_lastrowid_survives_cross_cursor_rollback.py +0 -0
  475. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_multiline_returning_rows.py +0 -0
  476. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_nan_inf_float.py +0 -0
  477. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_negative_zero_and_subnormal_float.py +0 -0
  478. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_no_transaction_error_wording.py +0 -0
  479. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_null_vs_empty_text_blob_three_way.py +0 -0
  480. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_pragma_read_rowcount_minus_one.py +0 -0
  481. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_query_detection.py +0 -0
  482. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_returning.py +0 -0
  483. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_savepoint_dbapi.py +0 -0
  484. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_savepoint_failure_responses.py +0 -0
  485. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_sqlite_version_pin.py +0 -0
  486. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/integration/test_sync_transaction_ctxmgr_rejects_stray_commit.py +0 -0
  487. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_aconn_execute_with_parameters.py +0 -0
  488. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_aconn_factory_getters.py +0 -0
  489. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_aio_description_tuple_exported.py +0 -0
  490. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_aio_register_adapter_export.py +0 -0
  491. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_aio_register_converter_stubs.py +0 -0
  492. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_async_backup_not_async_def.py +0 -0
  493. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_async_cursor_aclose_alias.py +0 -0
  494. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_async_execute_busy_retry_releases_op_lock_between_attempts.py +0 -0
  495. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_async_executescript_stub_raises_on_call.py +0 -0
  496. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_binary_typeerror_stdlib_parity.py +0 -0
  497. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_classify_caller_sql_hardening.py +0 -0
  498. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_conftest_adapter_registry_isolation.py +0 -0
  499. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_connect_closed_precedes_thread_affinity.py +0 -0
  500. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_connection_cursor_class_attribute.py +0 -0
  501. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_connection_executemany_outer_shape_check.py +0 -0
  502. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_cursor_close_preserves_body_exception_on_messages_strip.py +0 -0
  503. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_cursor_closed_after_connection_gc_pep249.py +0 -0
  504. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_cursor_execute_non_str_operation.py +0 -0
  505. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_cursor_executemany_non_str_operation.py +0 -0
  506. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_cursor_factory_kwarg_rejected.py +0 -0
  507. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_dbapi_default_timeouts_use_client_constants.py +0 -0
  508. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_dbapi_exception_message_arg_cap.py +0 -0
  509. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_dbapi_exception_raw_message_cap.py +0 -0
  510. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_dbapi_type_hash_consistency.py +0 -0
  511. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_description_tuple_public_alias.py +0 -0
  512. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_error_pickle_cause_either_or.py +0 -0
  513. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_error_pickle_preserves_raw_message.py +0 -0
  514. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_exception_repr_code.py +0 -0
  515. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_exceptions.py +0 -0
  516. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_exceptions_all_symmetry.py +0 -0
  517. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_execute_executemany_positional_only.py +0 -0
  518. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_force_close_observe_callback_coverage.py +0 -0
  519. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_fromticks_tz_policy.py +0 -0
  520. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_lastrowid_docstring_stdlib_parity_claim.py +0 -0
  521. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_leader_error_codes_identity.py +0 -0
  522. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_no_workflow_references_in_source.py +0 -0
  523. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_number_includes_boolean_documented.py +0 -0
  524. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_paramstyle_qmark_only_pin.py +0 -0
  525. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_parse_decltypes_colnames_constants.py +0 -0
  526. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_pep249_exception_aliases.py +0 -0
  527. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_pep249_type_constructors_vs_singletons.py +0 -0
  528. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_pep249_type_constructors_wrap_valueerror.py +0 -0
  529. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_pep249_type_objects_final_annotated.py +0 -0
  530. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_pickle_guard.py +0 -0
  531. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_prepare_protocol_in_types_all.py +0 -0
  532. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_pyproject_dependency_graph.py +0 -0
  533. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_readme_truthfulness_pins.py +0 -0
  534. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_register_adapter_global_sync_aio_symmetry.py +0 -0
  535. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_register_adapter_positional_only_signature.py +0 -0
  536. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_row_factory_annotation.py +0 -0
  537. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_row_factory_sqlite3_equivalent.py +0 -0
  538. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_row_hash_eq_empty.py +0 -0
  539. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_rowid_number_overlap_documented.py +0 -0
  540. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_setinputsizes_sequence_acceptance.py +0 -0
  541. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_sqlite_errorname_property.py +0 -0
  542. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_sqlite_version_not_public_attribute.py +0 -0
  543. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_stub_signatures_normalize_to_dbapi_error.py +0 -0
  544. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_sync_connection_execute_shortcut.py +0 -0
  545. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_sync_connection_executemany_shortcut.py +0 -0
  546. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_timeout_validation.py +0 -0
  547. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_top_level_logger_has_null_handler.py +0 -0
  548. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_transitive_wire_import.py +0 -0
  549. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_types_dbapi_compare.py +0 -0
  550. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_unregister_adapter.py +0 -0
  551. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_validate_ticks.py +0 -0
  552. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_version.py +0 -0
  553. {dqlite_dbapi-0.3.0 → dqlite_dbapi-0.4.0}/tests/test_version_export.py +0 -0
@@ -33,8 +33,8 @@ uv sync --extra dev
33
33
  # Run unit tests only
34
34
  .venv/bin/pytest tests/ --ignore=tests/integration
35
35
 
36
- # Run all tests (requires Docker cluster)
37
- cd ../dqlite-test-cluster && docker compose up -d
36
+ # Run all tests (requires the Docker cluster from the sibling python-dqlite-dev checkout)
37
+ (cd ../python-dqlite-dev/cluster && docker compose up -d)
38
38
  .venv/bin/pytest tests/
39
39
  ```
40
40
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: dqlite-dbapi
3
- Version: 0.3.0
3
+ Version: 0.4.0
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.4.0,>=0.3.0
26
- Requires-Dist: dqlite-wire<0.4.0,>=0.3.0
25
+ Requires-Dist: dqlite-client<0.5.0,>=0.4.0
26
+ Requires-Dist: dqlite-wire<0.5.0,>=0.4.0
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: mypy>=1.0; extra == 'dev'
29
29
  Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
@@ -121,6 +121,8 @@ Using SQLAlchemy or an ORM? Prefer
121
121
  gotcha).
122
122
  - [Differences from `aiosqlite`](docs/differences-from-aiosqlite.md) — for
123
123
  the async surface.
124
+ - [Architecture](docs/architecture.md) — how the sync surface wraps the
125
+ async core, and the threading, lifecycle, and error-mapping policies.
124
126
 
125
127
  ## Development
126
128
 
@@ -87,6 +87,8 @@ Using SQLAlchemy or an ORM? Prefer
87
87
  gotcha).
88
88
  - [Differences from `aiosqlite`](docs/differences-from-aiosqlite.md) — for
89
89
  the async surface.
90
+ - [Architecture](docs/architecture.md) — how the sync surface wraps the
91
+ async core, and the threading, lifecycle, and error-mapping policies.
90
92
 
91
93
  ## Development
92
94
 
@@ -0,0 +1,169 @@
1
+ # Architecture
2
+
3
+ This page records the design decisions behind `dqlite-dbapi`. The user-facing
4
+ behaviour they produce is documented in [Transactions](transactions.md) and
5
+ [Differences from `sqlite3`](differences-from-sqlite3.md).
6
+
7
+ ## Layering
8
+
9
+ ```
10
+ dqlitedbapi sync PEP 249 surface: Connection, Cursor
11
+ │ one daemon thread + asyncio loop per Connection
12
+ dqlitedbapi.aio async surface: AsyncConnection, AsyncCursor ← all driver logic
13
+ │
14
+ dqliteclient DqliteConnection (wire session), ClusterClient (leader discovery)
15
+ ```
16
+
17
+ All driver logic lives in `dqlitedbapi.aio`. The sync package is a thin
18
+ adapter: a sync `Connection` owns one `AsyncConnection` and one background
19
+ thread running an asyncio event loop; every sync method submits the matching
20
+ coroutine to that loop and blocks on the result. Fetch methods never hop to
21
+ the loop: results are fully buffered at execute time, so the sync cursor reads
22
+ the buffer directly.
23
+
24
+ Modules:
25
+
26
+ | Module | Responsibility |
27
+ | --- | --- |
28
+ | `aio/connection.py` | `AsyncConnection`: lifecycle, transactions, session mode, operation lock |
29
+ | `aio/cursor.py` | `AsyncCursor`: execute / executemany, result buffering, description |
30
+ | `connection.py`, `cursor.py` | sync adapters over the async classes |
31
+ | `_loop.py` | the background loop thread used by the sync adapter |
32
+ | `_sql.py` | statement classification (row-returning, DML verbs, BEGIN rewrite, PRAGMA intercept) |
33
+ | `_busy.py` | `SQLITE_BUSY` retry curve |
34
+ | `exceptions.py` | PEP 249 hierarchy and the client-error to dbapi-error mapping |
35
+ | `types.py`, `row.py` | type objects, constructors, adapters, datetime codecs, `Row` |
36
+
37
+ Lexical SQL helpers (comment stripping, top-level statement splitting,
38
+ literal blanking, leading keyword) come from `dqliteclient.sql`, which the
39
+ client also uses for its own transaction tracking. `_sql.py` holds only the
40
+ semantic rules the driver adds on top.
41
+
42
+ ## Connection lifecycle
43
+
44
+ - Constructing a connection validates its arguments and does no I/O.
45
+ - The first operation (or an explicit `connect()` / `aconnect()`) resolves
46
+ the cluster leader from the seed address through `ClusterClient`, opens the
47
+ wire session against the leader, and applies the session mode.
48
+ - **An invalidated connection is a closed connection.** When the underlying
49
+ wire session is lost (transport error, leader change, cancellation or
50
+ interrupt during an operation), `closed` becomes `True`, `invalidated`
51
+ becomes `True`, and every further operation raises `InterfaceError`. The
52
+ driver never reconnects silently: a reconnect would drop transaction state
53
+ the caller believes is still open. Pools (SQLAlchemy's included) discard the
54
+ connection and open a fresh one.
55
+ - `close()` is idempotent and does not roll back (stdlib `sqlite3` parity).
56
+ - `force_close_transport()` is synchronous, thread-safe, never raises, and
57
+ tears the socket down without waiting for an in-flight operation. It exists
58
+ for pool `terminate` paths and for shutting down from a thread that is not
59
+ the connection's owner.
60
+
61
+ ## Threading and concurrency
62
+
63
+ ### Sync surface
64
+
65
+ - `check_same_thread=True` (default): every method except
66
+ `force_close_transport()` must be called from the thread that created the
67
+ connection; other threads get `ProgrammingError`.
68
+ - `check_same_thread=False`: any thread may call the connection. Calls are
69
+ serialised by a lock. Cursors are still single-thread objects, as in
70
+ `sqlite3`: share the connection, give each thread its own cursor.
71
+ - **Interrupts close the connection.** A `KeyboardInterrupt` or `SystemExit`
72
+ delivered while a call is blocked cancels the in-flight operation,
73
+ force-closes the connection, and propagates. The driver must not be
74
+ re-entered from a signal handler; that case is out of contract.
75
+ - The sync side imposes no deadline of its own. Every await in the async
76
+ layer is already bounded by the client's per-RPC timeouts, so a blocked sync
77
+ call ends when those fire or when `force_close_transport()` is called from
78
+ another thread.
79
+
80
+ ### Async surface
81
+
82
+ - An `AsyncConnection` binds to the event loop it first runs on. Using it
83
+ from another loop raises `InterfaceError`; it cannot be shared across
84
+ `asyncio.run()` calls or threads.
85
+ - One operation at a time: an `asyncio.Lock` serialises execute, commit and
86
+ rollback across tasks on the same loop. Waiting tasks queue rather than
87
+ fail. A cursor is a single-task object.
88
+ - Cancellation during a wire round-trip invalidates the connection (see
89
+ above), because the server-side effect of the interrupted statement is
90
+ unknown.
91
+
92
+ ## Statements
93
+
94
+ - One statement per `execute()`; a second statement after `;` raises
95
+ `ProgrammingError` before any network round-trip. `CREATE TRIGGER` bodies
96
+ are understood, so their inner `;` do not count.
97
+ - Empty, comment-only, and NUL-containing SQL raise `ProgrammingError`.
98
+ - A `?` count that does not match the parameter count raises
99
+ `ProgrammingError` client-side.
100
+ - Row-returning statements (`SELECT`, `VALUES`, `PRAGMA`, `EXPLAIN`, a
101
+ `WITH` whose final statement is one of those, and any DML with
102
+ `RETURNING`) go through the query path and populate `description` and the
103
+ row buffer. Everything else goes through the exec path and populates
104
+ `rowcount` (INSERT/UPDATE/DELETE/REPLACE only, else -1) and `lastrowid`
105
+ (INSERT/REPLACE only, sticky across other statements).
106
+ - `PRAGMA busy_timeout` is answered locally: the server rejects it, and the
107
+ driver owns the busy budget.
108
+ - A bare `BEGIN` / `BEGIN TRANSACTION` is rewritten according to the session
109
+ mode: `immediate` → `BEGIN IMMEDIATE`, `exclusive` → `BEGIN EXCLUSIVE`,
110
+ `deferred` and `read_only` → unchanged. An explicit qualifier is never
111
+ rewritten.
112
+ - `SQLITE_BUSY` is retried on SQLite's default busy-callback curve until the
113
+ connection's `busy_timeout` budget is spent. Each `executemany` iteration
114
+ retries independently.
115
+ - `description` carries `(name, type_code, None, None, None, None, None)`;
116
+ the type code is the wire `ValueType` of the first non-NULL cell in the
117
+ column, or the `UNKNOWN` sentinel when there is none.
118
+ - `ISO8601` and `UNIXTIME` wire cells decode to `datetime` objects; every
119
+ other cell is passed through as the wire primitive.
120
+
121
+ ## Transactions
122
+
123
+ - Autocommit by default: no implicit `BEGIN`.
124
+ - `commit()` / `rollback()` are no-ops before the first connection and when no
125
+ transaction is open. A server reply of "no transaction is active" is
126
+ swallowed. Every other failure propagates.
127
+ - A `COMMIT` that fails with a leadership-lost code raises
128
+ `AmbiguousCommitError` (an `OperationalError`): the write may or may not
129
+ have been applied.
130
+ - `transaction()` is a context manager issuing `BEGIN` / `COMMIT` /
131
+ `ROLLBACK`. Calling `commit()` or `rollback()` inside the block raises
132
+ `InterfaceError`, because the block owns the boundaries.
133
+ - `with conn:` / `async with conn:` commit on success and roll back on
134
+ exception; neither closes the connection.
135
+ - `session_mode` is read-only; `set_session_mode()` changes it, emitting
136
+ `PRAGMA query_only` when crossing the `read_only` boundary. It may not be
137
+ called inside a transaction.
138
+
139
+ ## Errors
140
+
141
+ Client exceptions are translated at the boundary by `exceptions.translate`:
142
+
143
+ | Client exception | dbapi exception |
144
+ | --- | --- |
145
+ | `OperationalError` with code | class chosen from the SQLite primary code (constraint → `IntegrityError`, internal/nomem/notfound → `InternalError`, toobig → `DataError`, range/misuse → `InterfaceError`, corrupt/format/notadb → bare `DatabaseError`, dqlite parse/notfound → `ProgrammingError`, else `OperationalError`) |
146
+ | `DqliteConnectionError`, `ClusterError`, `ProtocolError` | `OperationalError` with `code=None` |
147
+ | `ClusterPolicyError` | `InterfaceError` prefixed `Cluster policy rejection` (never retried) |
148
+ | `DataError`, wire `EncodeError` | `DataError` |
149
+ | `InterfaceError` | `InterfaceError` |
150
+ | any other `DqliteError` | `DatabaseError` |
151
+
152
+ Every raised error keeps `code` and `raw_message` (the untruncated server
153
+ text). Connect-time failures are prefixed `Failed to connect: `. These
154
+ prefixes and the `Connection is closed` / `Cursor is closed` /
155
+ `invalidated (id=` wordings are load-bearing: `sqlalchemy-dqlite` classifies
156
+ disconnects on them.
157
+
158
+ ## Contract with sqlalchemy-dqlite
159
+
160
+ The dialect relies on exactly this dbapi surface, beyond PEP 249 itself:
161
+
162
+ - module: `connect`, exception classes, `paramstyle`, `sqlite_version_info`,
163
+ `FAILED_TO_CONNECT_PREFIX`, `CLUSTER_POLICY_REJECTION_PREFIX`;
164
+ - connection: `cursor()`, `commit()`, `rollback()`, `close()`,
165
+ `force_close_transport()`, `connect()` (async), `session_mode`,
166
+ `default_session_mode`, `set_session_mode()`, `in_transaction`;
167
+ - cursor: `execute()`, `executemany()`, `description`, `rowcount`,
168
+ `lastrowid`, `drain_rows()` (async), `close()`;
169
+ - `types.format_utc_offset()` for the dialect's datetime processors.
@@ -37,6 +37,15 @@ covers the differences you are most likely to notice when porting code.
37
37
  `create_window_function`, `iterdump`, `backup`, `set_authorizer`,
38
38
  `serialize`, `blobopen`, `executescript`, and `register_converter` have no
39
39
  server-side counterpart in dqlite; their stubs raise `NotSupportedError`.
40
+ - **A lost connection stays lost.** When the wire session drops (transport
41
+ error, leader change, or an interrupted call), `closed` becomes `True`,
42
+ `invalidated` becomes `True`, and every further call raises
43
+ `InterfaceError`. The driver never reconnects behind your back, because a
44
+ silent reconnect would drop transaction state; open a new connection (a
45
+ pool does this for you).
46
+ - **Bad `connect()` arguments raise `ProgrammingError`**, not `TypeError` /
47
+ `ValueError`, so configuration mistakes stay inside the `dbapi.Error`
48
+ hierarchy.
40
49
  - **`rowcount` after SELECT is `len(rows)`.** Stdlib returns `-1` for queries.
41
50
  dqlite knows the full result at execute time, so it reports the count.
42
51
  `rowcount` stays `-1` for non-result paths, for all PRAGMA statements (read
@@ -29,23 +29,35 @@ cur.execute("INSERT INTO t VALUES (?)", (2,))
29
29
  conn.commit() # or conn.rollback()
30
30
  ```
31
31
 
32
- Under the default session mode (`immediate`) a bare `BEGIN` / `BEGIN
33
- TRANSACTION` is rewritten to `BEGIN IMMEDIATE`, so the deferred-to-write
34
- upgrade can't fail later with `SQLITE_BUSY_SNAPSHOT`; an explicit `DEFERRED`
35
- / `IMMEDIATE` / `EXCLUSIVE` qualifier is passed through unchanged. dqlite's
36
- Raft FSM serializes the transaction across the cluster regardless of the
37
- qualifier, so the qualifier changes only lock-acquisition timing on the
38
- leader, never isolation.
39
-
40
- The session mode is selected with the `session_mode` connect argument (or the
41
- `DQLITE_SESSION_MODE` environment variable): `immediate` (default; rewrites a
42
- bare `BEGIN`), `deferred` / `exclusive` (no rewrite), or `read_only` (issues
43
- `PRAGMA query_only = 1`).
32
+ A bare `BEGIN` / `BEGIN TRANSACTION` is qualified according to the
33
+ connection's session mode; an explicit `DEFERRED` / `IMMEDIATE` /
34
+ `EXCLUSIVE` qualifier is always passed through unchanged. dqlite's Raft FSM
35
+ serializes the transaction across the cluster regardless of the qualifier, so
36
+ the qualifier changes only lock-acquisition timing on the leader, never
37
+ isolation.
38
+
39
+ | `session_mode` | bare `BEGIN` becomes | notes |
40
+ | --- | --- | --- |
41
+ | `immediate` (default) | `BEGIN IMMEDIATE` | takes the write lock up front, so a read-then-write cannot fail later with `SQLITE_BUSY_SNAPSHOT` |
42
+ | `deferred` | `BEGIN` | SQLite's default deferred transaction |
43
+ | `exclusive` | `BEGIN EXCLUSIVE` | |
44
+ | `read_only` | `BEGIN` | the connection runs with `PRAGMA query_only = 1`; writes raise `OperationalError` |
45
+
46
+ The mode is selected with the `session_mode` connect argument (or the
47
+ `DQLITE_SESSION_MODE` environment variable), read back through the
48
+ `session_mode` property, and changed at runtime with `set_session_mode()`,
49
+ which may not be called inside a transaction. `default_session_mode` is the
50
+ value given at construction.
44
51
 
45
52
  The connection also works as a context manager (matching stdlib `sqlite3`):
46
53
  the `with conn:` block commits on clean exit and rolls back on exception.
47
54
  It does **not** close the connection — the connection stays reusable.
48
55
 
56
+ `conn.transaction()` (sync) / `async with conn.transaction():` wraps a block
57
+ in `BEGIN` / `COMMIT` / `ROLLBACK`. Calling `commit()` or `rollback()`
58
+ inside the block raises `InterfaceError`, because the block owns the
59
+ boundaries; use `SAVEPOINT` for nesting.
60
+
49
61
  ## `commit()` / `rollback()` details
50
62
 
51
63
  - Calling `commit()` / `rollback()` before any query has run is a silent
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dqlite-dbapi"
7
- version = "0.3.0"
7
+ version = "0.4.0"
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.3.0,<0.4.0",
30
- "dqlite-wire>=0.3.0,<0.4.0",
29
+ "dqlite-client>=0.4.0,<0.5.0",
30
+ "dqlite-wire>=0.4.0,<0.5.0",
31
31
  ]
32
32
 
33
33
  [tool.uv.sources]
@@ -0,0 +1,156 @@
1
+ """PEP 249 (DB-API 2.0) driver for dqlite."""
2
+
3
+ import logging
4
+
5
+ from dqliteclient import DEFAULT_CLOSE_TIMEOUT_SECONDS, DEFAULT_TIMEOUT_SECONDS, DialFunc
6
+ from dqlitedbapi._module import (
7
+ LEGACY_TRANSACTION_CONTROL,
8
+ PARSE_COLNAMES,
9
+ PARSE_DECLTYPES,
10
+ __version__,
11
+ apilevel,
12
+ complete_statement,
13
+ enable_callback_tracebacks,
14
+ paramstyle,
15
+ register_converter,
16
+ sqlite_version,
17
+ sqlite_version_info,
18
+ threadsafety,
19
+ )
20
+ from dqlitedbapi.aio.connection import apply_stdlib_connect_kwargs as _apply_stdlib_connect_kwargs
21
+ from dqlitedbapi.connection import Connection
22
+ from dqlitedbapi.cursor import Cursor
23
+ from dqlitedbapi.exceptions import (
24
+ CLUSTER_POLICY_REJECTION_PREFIX,
25
+ FAILED_TO_CONNECT_PREFIX,
26
+ AdapterLookupError,
27
+ AmbiguousCommitError,
28
+ DatabaseError,
29
+ DataError,
30
+ Error,
31
+ IntegrityError,
32
+ InterfaceError,
33
+ InternalError,
34
+ NotSupportedError,
35
+ OperationalError,
36
+ ProgrammingError,
37
+ Warning,
38
+ )
39
+ from dqlitedbapi.row import Row
40
+ from dqlitedbapi.types import (
41
+ BINARY,
42
+ DATETIME,
43
+ NUMBER,
44
+ ROWID,
45
+ STRING,
46
+ UNKNOWN,
47
+ Binary,
48
+ Date,
49
+ DateFromTicks,
50
+ DescriptionTuple,
51
+ PrepareProtocol,
52
+ Time,
53
+ TimeFromTicks,
54
+ Timestamp,
55
+ TimestampFromTicks,
56
+ register_adapter,
57
+ unregister_adapter,
58
+ )
59
+ from dqlitewire import DEFAULT_MAX_CONTINUATION_FRAMES as _DEFAULT_MAX_CONTINUATION_FRAMES
60
+ from dqlitewire import DEFAULT_MAX_TOTAL_ROWS as _DEFAULT_MAX_TOTAL_ROWS
61
+
62
+ __all__ = [
63
+ "__version__",
64
+ "apilevel",
65
+ "threadsafety",
66
+ "paramstyle",
67
+ "sqlite_version",
68
+ "sqlite_version_info",
69
+ "LEGACY_TRANSACTION_CONTROL",
70
+ "PARSE_DECLTYPES",
71
+ "PARSE_COLNAMES",
72
+ "connect",
73
+ "Connection",
74
+ "Cursor",
75
+ "DialFunc",
76
+ "Warning",
77
+ "Error",
78
+ "InterfaceError",
79
+ "DatabaseError",
80
+ "DataError",
81
+ "OperationalError",
82
+ "AmbiguousCommitError",
83
+ "IntegrityError",
84
+ "InternalError",
85
+ "ProgrammingError",
86
+ "NotSupportedError",
87
+ "AdapterLookupError",
88
+ "Date",
89
+ "Time",
90
+ "Timestamp",
91
+ "DateFromTicks",
92
+ "TimeFromTicks",
93
+ "TimestampFromTicks",
94
+ "Binary",
95
+ "STRING",
96
+ "BINARY",
97
+ "NUMBER",
98
+ "DATETIME",
99
+ "ROWID",
100
+ "UNKNOWN",
101
+ "Row",
102
+ "DescriptionTuple",
103
+ "PrepareProtocol",
104
+ "register_adapter",
105
+ "unregister_adapter",
106
+ "register_converter",
107
+ "complete_statement",
108
+ "enable_callback_tracebacks",
109
+ "CLUSTER_POLICY_REJECTION_PREFIX",
110
+ "FAILED_TO_CONNECT_PREFIX",
111
+ ]
112
+
113
+
114
+ def connect(
115
+ address: str,
116
+ *,
117
+ database: str = "default",
118
+ timeout: float = DEFAULT_TIMEOUT_SECONDS,
119
+ max_total_rows: int | None = _DEFAULT_MAX_TOTAL_ROWS,
120
+ max_continuation_frames: int | None = _DEFAULT_MAX_CONTINUATION_FRAMES,
121
+ max_message_size: int | None = None,
122
+ trust_server_heartbeat: bool = False,
123
+ close_timeout: float = DEFAULT_CLOSE_TIMEOUT_SECONDS,
124
+ dial_timeout: float | None = None,
125
+ attempt_timeout: float | None = None,
126
+ dial_func: DialFunc | None = None,
127
+ busy_timeout: float = 5.0,
128
+ check_same_thread: bool = True,
129
+ session_mode: str | None = None,
130
+ **stdlib_kwargs: object,
131
+ ) -> Connection:
132
+ """Create a connection to the cluster reachable through ``address`` (``host:port``).
133
+
134
+ The wire session opens on first use; ``timeout`` bounds each RPC phase.
135
+ """
136
+ connection = Connection(
137
+ address,
138
+ database=database,
139
+ timeout=timeout,
140
+ max_total_rows=max_total_rows,
141
+ max_continuation_frames=max_continuation_frames,
142
+ max_message_size=max_message_size,
143
+ trust_server_heartbeat=trust_server_heartbeat,
144
+ close_timeout=close_timeout,
145
+ dial_timeout=dial_timeout,
146
+ attempt_timeout=attempt_timeout,
147
+ dial_func=dial_func,
148
+ busy_timeout=busy_timeout,
149
+ check_same_thread=check_same_thread,
150
+ session_mode=session_mode,
151
+ )
152
+ _apply_stdlib_connect_kwargs(connection._async, stdlib_kwargs)
153
+ return connection
154
+
155
+
156
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -0,0 +1,57 @@
1
+ """Retry on ``SQLITE_BUSY`` following SQLite's default busy-handler curve.
2
+
3
+ dqlite rejects ``PRAGMA busy_timeout`` on the wire, so the driver owns the budget.
4
+ """
5
+
6
+ import asyncio
7
+ import logging
8
+ from collections.abc import Awaitable, Callable
9
+ from typing import Final
10
+
11
+ from dqlitedbapi.exceptions import OperationalError
12
+ from dqlitewire import SQLITE_BUSY
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # sqlite3.c ``sqliteDefaultBusyCallback``: sleep before retry i, and cumulative sleep.
17
+ _DELAYS_MS: Final[tuple[int, ...]] = (1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100)
18
+ _TOTALS_MS: Final[tuple[int, ...]] = (0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228)
19
+
20
+
21
+ def next_delay_ms(attempt: int, budget_ms: int) -> int | None:
22
+ """Milliseconds to sleep before retry ``attempt + 1``, or ``None`` when the budget is spent."""
23
+ if budget_ms <= 0:
24
+ return None
25
+ if attempt < len(_DELAYS_MS):
26
+ delay, prior = _DELAYS_MS[attempt], _TOTALS_MS[attempt]
27
+ else:
28
+ delay = _DELAYS_MS[-1]
29
+ prior = _TOTALS_MS[-1] + delay * (attempt - (len(_DELAYS_MS) - 1))
30
+ if prior + delay > budget_ms:
31
+ delay = budget_ms - prior
32
+ return delay if delay > 0 else None
33
+
34
+
35
+ def is_busy(exc: BaseException) -> bool:
36
+ return isinstance(exc, OperationalError) and exc.code == SQLITE_BUSY
37
+
38
+
39
+ async def retry_on_busy[T](busy_timeout: float, attempt: Callable[[], Awaitable[T]]) -> T:
40
+ """Run ``attempt()`` until it succeeds, fails with a non-BUSY error, or the budget is spent."""
41
+ budget_ms = round(busy_timeout * 1000)
42
+ count = 0
43
+ while True:
44
+ try:
45
+ return await attempt()
46
+ except OperationalError as exc:
47
+ if not is_busy(exc):
48
+ raise
49
+ delay = next_delay_ms(count, budget_ms)
50
+ if delay is None:
51
+ if budget_ms > 0:
52
+ logger.warning(
53
+ "busy_timeout budget %.3fs exhausted after %d retries", busy_timeout, count
54
+ )
55
+ raise
56
+ await asyncio.sleep(delay / 1000)
57
+ count += 1
@@ -0,0 +1,105 @@
1
+ """Background event-loop thread used by the sync surface."""
2
+
3
+ import asyncio
4
+ import concurrent.futures
5
+ import contextlib
6
+ import threading
7
+ from collections.abc import Coroutine
8
+ from typing import Any
9
+
10
+ from dqlitedbapi.exceptions import InterfaceError
11
+
12
+
13
+ class LoopThread:
14
+ """A daemon thread running an asyncio loop; coroutines are submitted and awaited from
15
+ other threads. Started on first use, stopped by :meth:`shutdown`."""
16
+
17
+ def __init__(self, name: str) -> None:
18
+ self._name = name
19
+ self._loop: asyncio.AbstractEventLoop | None = None
20
+ self._thread: threading.Thread | None = None
21
+ self._inflight: set[concurrent.futures.Future[Any]] = set()
22
+ self._guard = threading.Lock()
23
+
24
+ @property
25
+ def loop(self) -> asyncio.AbstractEventLoop | None:
26
+ return self._loop
27
+
28
+ def run[T](self, coro: Coroutine[Any, Any, T]) -> T:
29
+ """Run ``coro`` on the loop thread and block for its result.
30
+
31
+ A ``KeyboardInterrupt`` or ``SystemExit`` while blocked cancels the coroutine before
32
+ propagating. A cancellation triggered by :meth:`shutdown` surfaces as
33
+ ``concurrent.futures.CancelledError``.
34
+ """
35
+ try:
36
+ loop = self._ensure_running()
37
+ except BaseException:
38
+ coro.close()
39
+ raise
40
+ future = asyncio.run_coroutine_threadsafe(coro, loop)
41
+ with self._guard:
42
+ self._inflight.add(future)
43
+ try:
44
+ return future.result()
45
+ except BaseException:
46
+ future.cancel()
47
+ raise
48
+ finally:
49
+ with self._guard:
50
+ self._inflight.discard(future)
51
+
52
+ def shutdown(self, timeout: float) -> None:
53
+ """Cancel in-flight work, stop the loop and join the thread. Idempotent, never raises."""
54
+ with self._guard:
55
+ loop, thread = self._loop, self._thread
56
+ self._thread = None
57
+ if loop is None:
58
+ return
59
+ if thread is not None and thread.is_alive():
60
+ stopper = _cancel_all_and_stop(loop, timeout)
61
+ try:
62
+ asyncio.run_coroutine_threadsafe(stopper, loop)
63
+ except RuntimeError:
64
+ stopper.close()
65
+ thread.join(timeout + 1.0)
66
+ with self._guard:
67
+ inflight = list(self._inflight)
68
+ for future in inflight:
69
+ with contextlib.suppress(concurrent.futures.InvalidStateError):
70
+ future.set_exception(
71
+ InterfaceError("Connection closed while an operation was in flight")
72
+ )
73
+ if not loop.is_running():
74
+ with contextlib.suppress(Exception):
75
+ loop.close()
76
+
77
+ def _ensure_running(self) -> asyncio.AbstractEventLoop:
78
+ with self._guard:
79
+ if self._thread is not None and self._thread.is_alive() and self._loop is not None:
80
+ return self._loop
81
+ if self._loop is not None and self._loop.is_closed():
82
+ raise InterfaceError("Connection is closed (event loop thread stopped)")
83
+ loop = asyncio.new_event_loop()
84
+ started = threading.Event()
85
+
86
+ def main() -> None:
87
+ asyncio.set_event_loop(loop)
88
+ loop.call_soon(started.set)
89
+ loop.run_forever()
90
+
91
+ thread = threading.Thread(target=main, name=self._name, daemon=True)
92
+ thread.start()
93
+ started.wait()
94
+ self._loop, self._thread = loop, thread
95
+ return loop
96
+
97
+
98
+ async def _cancel_all_and_stop(loop: asyncio.AbstractEventLoop, timeout: float) -> None:
99
+ me = asyncio.current_task()
100
+ tasks = [t for t in asyncio.all_tasks(loop) if t is not me]
101
+ for task in tasks:
102
+ task.cancel()
103
+ if tasks:
104
+ await asyncio.wait(tasks, timeout=timeout)
105
+ loop.stop()
@@ -0,0 +1,35 @@
1
+ """Module-level PEP 249 attributes shared by ``dqlitedbapi`` and ``dqlitedbapi.aio``."""
2
+
3
+ from typing import Final, Literal, NoReturn
4
+
5
+ from dqlitedbapi.exceptions import NotSupportedError
6
+
7
+ __version__: Final[str] = "0.4.0"
8
+
9
+ apilevel: Final[Literal["2.0"]] = "2.0"
10
+ threadsafety: Final[Literal[2]] = 2
11
+ paramstyle: Final[Literal["qmark"]] = "qmark"
12
+
13
+ # The SQLite feature floor every supported dqlite server provides (RETURNING needs 3.35).
14
+ sqlite_version_info: Final[tuple[int, int, int]] = (3, 35, 0)
15
+ sqlite_version: Final[str] = "3.35.0"
16
+
17
+ # stdlib sqlite3 parity constants; ``PARSE_*`` are accepted nowhere and enable nothing.
18
+ LEGACY_TRANSACTION_CONTROL: Final[int] = -1
19
+ PARSE_DECLTYPES: Final[int] = 1
20
+ PARSE_COLNAMES: Final[int] = 2
21
+
22
+
23
+ def register_converter(*args: object, **kwargs: object) -> NoReturn:
24
+ raise NotSupportedError(
25
+ "register_converter is not supported: the wire carries no declared column types; "
26
+ "use row_factory to post-process rows"
27
+ )
28
+
29
+
30
+ def complete_statement(*args: object, **kwargs: object) -> NoReturn:
31
+ raise NotSupportedError("complete_statement is not supported")
32
+
33
+
34
+ def enable_callback_tracebacks(*args: object, **kwargs: object) -> NoReturn:
35
+ raise NotSupportedError("enable_callback_tracebacks is not supported: there are no callbacks")