dqlite-dbapi 0.1.2__tar.gz → 0.2.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 (486) hide show
  1. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/DEVELOPMENT.md +2 -3
  2. dqlite_dbapi-0.2.0/PKG-INFO +369 -0
  3. dqlite_dbapi-0.2.0/README.md +335 -0
  4. dqlite_dbapi-0.2.0/pyproject.toml +114 -0
  5. dqlite_dbapi-0.2.0/src/dqlitedbapi/__init__.py +451 -0
  6. dqlite_dbapi-0.2.0/src/dqlitedbapi/_constants.py +88 -0
  7. dqlite_dbapi-0.2.0/src/dqlitedbapi/aio/__init__.py +523 -0
  8. dqlite_dbapi-0.2.0/src/dqlitedbapi/aio/connection.py +2782 -0
  9. dqlite_dbapi-0.2.0/src/dqlitedbapi/aio/cursor.py +1533 -0
  10. dqlite_dbapi-0.2.0/src/dqlitedbapi/connection.py +3948 -0
  11. dqlite_dbapi-0.2.0/src/dqlitedbapi/cursor.py +2741 -0
  12. dqlite_dbapi-0.2.0/src/dqlitedbapi/exceptions.py +433 -0
  13. dqlite_dbapi-0.2.0/src/dqlitedbapi/types.py +1008 -0
  14. dqlite_dbapi-0.2.0/tests/aio/test_aio_version_final_annotated.py +26 -0
  15. dqlite_dbapi-0.2.0/tests/aio/test_async_close_underlying_close_failed_logged.py +144 -0
  16. dqlite_dbapi-0.2.0/tests/aio/test_async_connection_fork_guard.py +136 -0
  17. dqlite_dbapi-0.2.0/tests/aio/test_async_iter_protocol_invariants.py +41 -0
  18. dqlite_dbapi-0.2.0/tests/aio/test_async_unclosed_warning_skips_in_fork_child.py +181 -0
  19. dqlite_dbapi-0.2.0/tests/aio/test_close_cancel_between_cursor_cascade_and_lock.py +71 -0
  20. dqlite_dbapi-0.2.0/tests/aio/test_fetchmany_cancel_atomic.py +87 -0
  21. dqlite_dbapi-0.2.0/tests/aio/test_messages_clear_on_closed.py +56 -0
  22. dqlite_dbapi-0.2.0/tests/conftest.py +60 -0
  23. dqlite_dbapi-0.2.0/tests/integration/test_async_concurrent_executemany_serialized.py +95 -0
  24. dqlite_dbapi-0.2.0/tests/integration/test_async_cursor_concurrent_execute_rejected.py +74 -0
  25. dqlite_dbapi-0.2.0/tests/integration/test_async_cursor_edge_branches.py +81 -0
  26. dqlite_dbapi-0.2.0/tests/integration/test_async_fetchmany_cancel.py +87 -0
  27. dqlite_dbapi-0.2.0/tests/integration/test_async_returning_zero_rows.py +43 -0
  28. dqlite_dbapi-0.2.0/tests/integration/test_async_transaction_ctxmgr_rejects_stray_commit.py +71 -0
  29. dqlite_dbapi-0.2.0/tests/integration/test_close_implicit_rollback_pep249.py +148 -0
  30. dqlite_dbapi-0.2.0/tests/integration/test_concurrent_cursors_visibility.py +52 -0
  31. dqlite_dbapi-0.2.0/tests/integration/test_constraint_extended_codes.py +104 -0
  32. dqlite_dbapi-0.2.0/tests/integration/test_context_manager.py +56 -0
  33. dqlite_dbapi-0.2.0/tests/integration/test_cursor_fetch_after_non_row_execute.py +113 -0
  34. dqlite_dbapi-0.2.0/tests/integration/test_cursor_misc_invariant_pins.py +90 -0
  35. dqlite_dbapi-0.2.0/tests/integration/test_cursor_row_index_after_fetchall.py +110 -0
  36. dqlite_dbapi-0.2.0/tests/integration/test_datetime_conversion.py +300 -0
  37. dqlite_dbapi-0.2.0/tests/integration/test_description_after_ddl_pragma.py +107 -0
  38. dqlite_dbapi-0.2.0/tests/integration/test_description_survives_empty_fetchall.py +126 -0
  39. dqlite_dbapi-0.2.0/tests/integration/test_executemany_cancel_mid_batch.py +115 -0
  40. dqlite_dbapi-0.2.0/tests/integration/test_executemany_completed_iterations_observability.py +69 -0
  41. dqlite_dbapi-0.2.0/tests/integration/test_executemany_cte_dml.py +74 -0
  42. dqlite_dbapi-0.2.0/tests/integration/test_executemany_failure_resets_rowcount.py +76 -0
  43. dqlite_dbapi-0.2.0/tests/integration/test_executemany_generator.py +51 -0
  44. dqlite_dbapi-0.2.0/tests/integration/test_executemany_returning.py +43 -0
  45. dqlite_dbapi-0.2.0/tests/integration/test_integrity_error.py +49 -0
  46. dqlite_dbapi-0.2.0/tests/integration/test_lastrowid_autoincrement.py +91 -0
  47. dqlite_dbapi-0.2.0/tests/integration/test_lastrowid_cursor_scoped.py +89 -0
  48. dqlite_dbapi-0.2.0/tests/integration/test_lastrowid_returning.py +65 -0
  49. dqlite_dbapi-0.2.0/tests/integration/test_lastrowid_survives_cross_cursor_rollback.py +129 -0
  50. dqlite_dbapi-0.2.0/tests/integration/test_leader_redirect_live.py +142 -0
  51. dqlite_dbapi-0.2.0/tests/integration/test_misc_coverage.py +401 -0
  52. dqlite_dbapi-0.2.0/tests/integration/test_multiline_returning_rows.py +73 -0
  53. dqlite_dbapi-0.2.0/tests/integration/test_nan_inf_float.py +69 -0
  54. dqlite_dbapi-0.2.0/tests/integration/test_negative_zero_and_subnormal_float.py +97 -0
  55. dqlite_dbapi-0.2.0/tests/integration/test_no_transaction_error_wording.py +127 -0
  56. dqlite_dbapi-0.2.0/tests/integration/test_null_vs_empty_text_blob_three_way.py +91 -0
  57. dqlite_dbapi-0.2.0/tests/integration/test_pragma_deny_list.py +60 -0
  58. dqlite_dbapi-0.2.0/tests/integration/test_query_detection.py +66 -0
  59. dqlite_dbapi-0.2.0/tests/integration/test_returning.py +98 -0
  60. dqlite_dbapi-0.2.0/tests/integration/test_savepoint_dbapi.py +155 -0
  61. dqlite_dbapi-0.2.0/tests/integration/test_savepoint_failure_responses.py +95 -0
  62. dqlite_dbapi-0.2.0/tests/integration/test_sqlite_version_pin.py +73 -0
  63. dqlite_dbapi-0.2.0/tests/integration/test_sync_transaction_ctxmgr_rejects_stray_commit.py +120 -0
  64. dqlite_dbapi-0.2.0/tests/test_aconn_execute_with_parameters.py +59 -0
  65. dqlite_dbapi-0.2.0/tests/test_aconn_factory_getters.py +54 -0
  66. dqlite_dbapi-0.2.0/tests/test_aconnect_cleanup_close_shielded_against_outer_timeout.py +91 -0
  67. dqlite_dbapi-0.2.0/tests/test_aconnect_cleanup_on_connect_failure.py +91 -0
  68. dqlite_dbapi-0.2.0/tests/test_adapter_exceptions_wrapped_as_data_error.py +175 -0
  69. dqlite_dbapi-0.2.0/tests/test_aenter_cleanup_preserves_original_under_outer_cancel.py +87 -0
  70. dqlite_dbapi-0.2.0/tests/test_aexit_behavioural_no_close.py +90 -0
  71. dqlite_dbapi-0.2.0/tests/test_aexit_commit_cancel_debug_log.py +112 -0
  72. dqlite_dbapi-0.2.0/tests/test_aexit_rollback_cancel_body_context_pin.py +97 -0
  73. dqlite_dbapi-0.2.0/tests/test_aexit_rollback_debug_log.py +71 -0
  74. dqlite_dbapi-0.2.0/tests/test_aio_commit_rollback_no_tx.py +76 -0
  75. dqlite_dbapi-0.2.0/tests/test_aio_commit_rollback_no_tx_code_matrix.py +101 -0
  76. dqlite_dbapi-0.2.0/tests/test_aio_commit_rollback_wrapping.py +149 -0
  77. dqlite_dbapi-0.2.0/tests/test_aio_connection_finalizer_detached_on_cancel_close.py +75 -0
  78. dqlite_dbapi-0.2.0/tests/test_aio_connection_finalizer_detached_on_close.py +79 -0
  79. dqlite_dbapi-0.2.0/tests/test_aio_cursor_drain_rows.py +91 -0
  80. dqlite_dbapi-0.2.0/tests/test_aio_cursor_row_factory_typeerror_wrap.py +113 -0
  81. dqlite_dbapi-0.2.0/tests/test_aio_description_tuple_exported.py +23 -0
  82. dqlite_dbapi-0.2.0/tests/test_aio_executemany_cursor_close_cascade.py +36 -0
  83. dqlite_dbapi-0.2.0/tests/test_aio_force_close_transport_cursor_cascade.py +102 -0
  84. dqlite_dbapi-0.2.0/tests/test_aio_init_reexports_diagnostic_prefixes.py +40 -0
  85. dqlite_dbapi-0.2.0/tests/test_aio_loop_affinity_exc_class.py +70 -0
  86. dqlite_dbapi-0.2.0/tests/test_aio_module_attributes.py +80 -0
  87. dqlite_dbapi-0.2.0/tests/test_aio_register_adapter_export.py +47 -0
  88. dqlite_dbapi-0.2.0/tests/test_aio_register_converter_stubs.py +36 -0
  89. dqlite_dbapi-0.2.0/tests/test_aio_row_factory_setter_loop_binding.py +109 -0
  90. dqlite_dbapi-0.2.0/tests/test_aio_setters_clear_messages_pep249.py +45 -0
  91. dqlite_dbapi-0.2.0/tests/test_arraysize_validation.py +109 -0
  92. dqlite_dbapi-0.2.0/tests/test_async_backup_not_async_def.py +34 -0
  93. dqlite_dbapi-0.2.0/tests/test_async_check_loop_only_pid_before_loop.py +87 -0
  94. dqlite_dbapi-0.2.0/tests/test_async_close_cancel_arm_cancels_inner_pending_drain.py +121 -0
  95. dqlite_dbapi-0.2.0/tests/test_async_close_ensure_locks_guard.py +45 -0
  96. dqlite_dbapi-0.2.0/tests/test_async_close_finally_propagates_cancel.py +163 -0
  97. dqlite_dbapi-0.2.0/tests/test_async_close_op_lock_bounded.py +147 -0
  98. dqlite_dbapi-0.2.0/tests/test_async_close_outer_cancel_force_closes_transport.py +119 -0
  99. dqlite_dbapi-0.2.0/tests/test_async_close_race.py +120 -0
  100. dqlite_dbapi-0.2.0/tests/test_async_commit_rollback_force_close_on_outer_cancel.py +125 -0
  101. dqlite_dbapi-0.2.0/tests/test_async_commit_rollback_in_transaction_under_lock.py +102 -0
  102. dqlite_dbapi-0.2.0/tests/test_async_commit_rollback_loop_binding_first.py +88 -0
  103. dqlite_dbapi-0.2.0/tests/test_async_commit_rollback_messages_under_lock.py +87 -0
  104. dqlite_dbapi-0.2.0/tests/test_async_commit_rollback_op_lock_bounded.py +67 -0
  105. dqlite_dbapi-0.2.0/tests/test_async_commit_rollback_phase_budget.py +109 -0
  106. dqlite_dbapi-0.2.0/tests/test_async_commit_rollback_retry_after_invalidate.py +143 -0
  107. dqlite_dbapi-0.2.0/tests/test_async_commit_rollback_timeout_phase_aware.py +118 -0
  108. dqlite_dbapi-0.2.0/tests/test_async_connect_cross_loop_diagnostic_on_fast_path.py +65 -0
  109. dqlite_dbapi-0.2.0/tests/test_async_connection.py +82 -0
  110. dqlite_dbapi-0.2.0/tests/test_async_connection_close_during_connect.py +72 -0
  111. dqlite_dbapi-0.2.0/tests/test_async_connection_cursor_fork_guard.py +82 -0
  112. dqlite_dbapi-0.2.0/tests/test_async_connection_ensure_connection_post_yield_close_shielded.py +188 -0
  113. dqlite_dbapi-0.2.0/tests/test_async_connection_race.py +44 -0
  114. dqlite_dbapi-0.2.0/tests/test_async_connection_setters_loop_binding.py +68 -0
  115. dqlite_dbapi-0.2.0/tests/test_async_connection_transaction.py +59 -0
  116. dqlite_dbapi-0.2.0/tests/test_async_cursor.py +270 -0
  117. dqlite_dbapi-0.2.0/tests/test_async_cursor_aclose_alias.py +98 -0
  118. dqlite_dbapi-0.2.0/tests/test_async_cursor_add_races_cascade_clear.py +151 -0
  119. dqlite_dbapi-0.2.0/tests/test_async_cursor_aenter_cross_task_executing_guard.py +80 -0
  120. dqlite_dbapi-0.2.0/tests/test_async_cursor_aenter_gc_parent_pep249.py +32 -0
  121. dqlite_dbapi-0.2.0/tests/test_async_cursor_aiter_aenter_pep249_pins.py +98 -0
  122. dqlite_dbapi-0.2.0/tests/test_async_cursor_aiter_cross_loop_fail_fast.py +68 -0
  123. dqlite_dbapi-0.2.0/tests/test_async_cursor_arraysize_row_factory_loop_binding.py +79 -0
  124. dqlite_dbapi-0.2.0/tests/test_async_cursor_callproc_nextset_scroll_loop_binding.py +91 -0
  125. dqlite_dbapi-0.2.0/tests/test_async_cursor_close_clears_executing_task.py +80 -0
  126. dqlite_dbapi-0.2.0/tests/test_async_cursor_close_is_sync_and_race_safe.py +137 -0
  127. dqlite_dbapi-0.2.0/tests/test_async_cursor_close_lock_free.py +75 -0
  128. dqlite_dbapi-0.2.0/tests/test_async_cursor_closed_state_pep249.py +110 -0
  129. dqlite_dbapi-0.2.0/tests/test_async_cursor_execute_executing_task_inside_try_frame.py +63 -0
  130. dqlite_dbapi-0.2.0/tests/test_async_cursor_execute_messages_clear.py +82 -0
  131. dqlite_dbapi-0.2.0/tests/test_async_cursor_executemany_validation_clears_executing_task.py +118 -0
  132. dqlite_dbapi-0.2.0/tests/test_async_cursor_fetch_loop_binding.py +146 -0
  133. dqlite_dbapi-0.2.0/tests/test_async_cursor_finally_clear_executing_task_unconditional.py +48 -0
  134. dqlite_dbapi-0.2.0/tests/test_async_cursor_late_add_proxy_swap.py +52 -0
  135. dqlite_dbapi-0.2.0/tests/test_async_cursor_loop_binding.py +61 -0
  136. dqlite_dbapi-0.2.0/tests/test_async_cursor_rownumber_closed_cross_loop.py +68 -0
  137. dqlite_dbapi-0.2.0/tests/test_async_cursor_setinputsizes_loop_binding.py +54 -0
  138. dqlite_dbapi-0.2.0/tests/test_async_executemany_atomic.py +63 -0
  139. dqlite_dbapi-0.2.0/tests/test_async_executemany_close_race.py +72 -0
  140. dqlite_dbapi-0.2.0/tests/test_async_executescript_stub_raises_on_call.py +61 -0
  141. dqlite_dbapi-0.2.0/tests/test_async_fetch_does_not_lazy_bind_loop.py +89 -0
  142. dqlite_dbapi-0.2.0/tests/test_async_fetchall_row_factory_happy_path.py +65 -0
  143. dqlite_dbapi-0.2.0/tests/test_async_force_close_transport.py +200 -0
  144. dqlite_dbapi-0.2.0/tests/test_async_in_transaction_closed_foreign_loop.py +75 -0
  145. dqlite_dbapi-0.2.0/tests/test_async_in_transaction_loop_binding.py +80 -0
  146. dqlite_dbapi-0.2.0/tests/test_async_invalidated_property.py +58 -0
  147. dqlite_dbapi-0.2.0/tests/test_async_lock_binding.py +143 -0
  148. dqlite_dbapi-0.2.0/tests/test_async_resource_warning_gate.py +144 -0
  149. dqlite_dbapi-0.2.0/tests/test_async_secondary_methods_clear_messages_before_guards.py +144 -0
  150. dqlite_dbapi-0.2.0/tests/test_async_transaction_owner_set_inside_try_frame.py +185 -0
  151. dqlite_dbapi-0.2.0/tests/test_async_with_exit_does_not_close.py +83 -0
  152. dqlite_dbapi-0.2.0/tests/test_audit_2026_05_coverage_gaps.py +293 -0
  153. dqlite_dbapi-0.2.0/tests/test_audit_2026_05_dbapi_coverage.py +421 -0
  154. dqlite_dbapi-0.2.0/tests/test_autocommit_doc_no_round_trip.py +69 -0
  155. dqlite_dbapi-0.2.0/tests/test_autocommit_legacy_transaction_control.py +68 -0
  156. dqlite_dbapi-0.2.0/tests/test_autocommit_property.py +83 -0
  157. dqlite_dbapi-0.2.0/tests/test_autocommit_setter_exact_int_gate.py +114 -0
  158. dqlite_dbapi-0.2.0/tests/test_binary_typeerror_stdlib_parity.py +53 -0
  159. dqlite_dbapi-0.2.0/tests/test_bind_typeerror_as_dataerror.py +53 -0
  160. dqlite_dbapi-0.2.0/tests/test_build_and_connect_base_exception_group_wrap.py +123 -0
  161. dqlite_dbapi-0.2.0/tests/test_build_and_connect_resolve_leader_oserror_wrap.py +84 -0
  162. dqlite_dbapi-0.2.0/tests/test_build_and_connect_wire_encode_error_translation.py +100 -0
  163. dqlite_dbapi-0.2.0/tests/test_call_client_base_exception_group_wrap.py +91 -0
  164. dqlite_dbapi-0.2.0/tests/test_call_client_baseexceptiongroup_cancel_split.py +110 -0
  165. dqlite_dbapi-0.2.0/tests/test_call_client_docstring_mapping_table.py +77 -0
  166. dqlite_dbapi-0.2.0/tests/test_call_client_mapping.py +155 -0
  167. dqlite_dbapi-0.2.0/tests/test_call_client_typeerror_message_does_not_blame_bind.py +43 -0
  168. dqlite_dbapi-0.2.0/tests/test_call_client_wire_encode_error.py +36 -0
  169. dqlite_dbapi-0.2.0/tests/test_callproc_noreturn_annotation.py +44 -0
  170. dqlite_dbapi-0.2.0/tests/test_callproc_scroll_clear_messages.py +88 -0
  171. dqlite_dbapi-0.2.0/tests/test_cascade_cursors_clears_messages.py +98 -0
  172. dqlite_dbapi-0.2.0/tests/test_classify_caller_sql.py +99 -0
  173. dqlite_dbapi-0.2.0/tests/test_classify_caller_sql_hardening.py +142 -0
  174. dqlite_dbapi-0.2.0/tests/test_classify_caller_sql_structural_reject_precedence.py +100 -0
  175. dqlite_dbapi-0.2.0/tests/test_classify_operational_code_zero.py +29 -0
  176. dqlite_dbapi-0.2.0/tests/test_cleanup_loop_thread_disarms_inner_and_reaps_drain.py +304 -0
  177. dqlite_dbapi-0.2.0/tests/test_cleanup_loop_thread_finalizer_fork_safe.py +191 -0
  178. dqlite_dbapi-0.2.0/tests/test_close_bypass_path_releases_latched_op_lock.py +72 -0
  179. dqlite_dbapi-0.2.0/tests/test_close_resource_leak.py +35 -0
  180. dqlite_dbapi-0.2.0/tests/test_close_signal_handler_reentry.py +177 -0
  181. dqlite_dbapi-0.2.0/tests/test_close_surfaces_op_lock_contention.py +96 -0
  182. dqlite_dbapi-0.2.0/tests/test_close_thread_join_uses_close_timeout.py +186 -0
  183. dqlite_dbapi-0.2.0/tests/test_close_timeout_floor_enforcement.py +59 -0
  184. dqlite_dbapi-0.2.0/tests/test_close_timeout_plumbing.py +113 -0
  185. dqlite_dbapi-0.2.0/tests/test_closed_property.py +104 -0
  186. dqlite_dbapi-0.2.0/tests/test_cluster_policy_rejection_prefix_constant.py +67 -0
  187. dqlite_dbapi-0.2.0/tests/test_code_to_exception_wire_ssot.py +58 -0
  188. dqlite_dbapi-0.2.0/tests/test_collated_coverage_gaps.py +193 -0
  189. dqlite_dbapi-0.2.0/tests/test_commit_after_external_close.py +83 -0
  190. dqlite_dbapi-0.2.0/tests/test_commit_leader_flip_ambiguous_commit_error.py +82 -0
  191. dqlite_dbapi-0.2.0/tests/test_commit_leader_flip_propagates.py +136 -0
  192. dqlite_dbapi-0.2.0/tests/test_commit_no_spurious_connect.py +72 -0
  193. dqlite_dbapi-0.2.0/tests/test_commit_rollback_shortcircuit_uses_in_transaction.py +182 -0
  194. dqlite_dbapi-0.2.0/tests/test_conform_adapter_non_primitive_rejected_at_dbapi_layer.py +83 -0
  195. dqlite_dbapi-0.2.0/tests/test_conftest_adapter_registry_isolation.py +31 -0
  196. dqlite_dbapi-0.2.0/tests/test_connect_baseexceptiongroup_cancel_split.py +108 -0
  197. dqlite_dbapi-0.2.0/tests/test_connect_clears_messages.py +46 -0
  198. dqlite_dbapi-0.2.0/tests/test_connect_closed_precedes_thread_affinity.py +82 -0
  199. dqlite_dbapi-0.2.0/tests/test_connect_docstring_parity.py +36 -0
  200. dqlite_dbapi-0.2.0/tests/test_connect_iso_autocommit_round_trip.py +115 -0
  201. dqlite_dbapi-0.2.0/tests/test_connect_isolation_level_autocommit_kwargs_symmetric.py +141 -0
  202. dqlite_dbapi-0.2.0/tests/test_connect_preserves_code.py +87 -0
  203. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/tests/test_connection.py +12 -3
  204. dqlite_dbapi-0.2.0/tests/test_connection_after_fork_raises.py +157 -0
  205. dqlite_dbapi-0.2.0/tests/test_connection_autocommit_isolation_level_closed_state.py +95 -0
  206. dqlite_dbapi-0.2.0/tests/test_connection_close_cascades_cursors.py +76 -0
  207. dqlite_dbapi-0.2.0/tests/test_connection_close_cursor_cascade_atomicity.py +74 -0
  208. dqlite_dbapi-0.2.0/tests/test_connection_close_idempotent_cross_thread.py +72 -0
  209. dqlite_dbapi-0.2.0/tests/test_connection_closed_checks.py +293 -0
  210. dqlite_dbapi-0.2.0/tests/test_connection_cursor_class_attribute.py +61 -0
  211. dqlite_dbapi-0.2.0/tests/test_connection_cursor_closed_state_precedes_unknown_kwarg.py +43 -0
  212. dqlite_dbapi-0.2.0/tests/test_connection_database_empty_string_rejected.py +56 -0
  213. dqlite_dbapi-0.2.0/tests/test_connection_database_kwarg_isinstance.py +45 -0
  214. dqlite_dbapi-0.2.0/tests/test_connection_eager_address_validation.py +52 -0
  215. dqlite_dbapi-0.2.0/tests/test_connection_enter_eager.py +44 -0
  216. dqlite_dbapi-0.2.0/tests/test_connection_executemany_outer_shape_check.py +214 -0
  217. dqlite_dbapi-0.2.0/tests/test_connection_exit_commit_ki_breadcrumb.py +103 -0
  218. dqlite_dbapi-0.2.0/tests/test_connection_exit_short_circuits_on_closed.py +104 -0
  219. dqlite_dbapi-0.2.0/tests/test_connection_messages_cleared_by_cursor_methods.py +65 -0
  220. dqlite_dbapi-0.2.0/tests/test_connection_row_factory_setter_closed_check.py +57 -0
  221. dqlite_dbapi-0.2.0/tests/test_context_manager_exit.py +103 -0
  222. dqlite_dbapi-0.2.0/tests/test_convert_bind_param_conform_exception_propagates.py +77 -0
  223. dqlite_dbapi-0.2.0/tests/test_cte_prefix_stripper_edges.py +117 -0
  224. dqlite_dbapi-0.2.0/tests/test_cursor.py +234 -0
  225. dqlite_dbapi-0.2.0/tests/test_cursor_after_external_close.py +63 -0
  226. dqlite_dbapi-0.2.0/tests/test_cursor_close_clears_messages.py +38 -0
  227. dqlite_dbapi-0.2.0/tests/test_cursor_close_cross_thread.py +48 -0
  228. dqlite_dbapi-0.2.0/tests/test_cursor_close_preserves_body_exception_on_messages_strip.py +82 -0
  229. dqlite_dbapi-0.2.0/tests/test_cursor_close_releases_connection_pin.py +64 -0
  230. dqlite_dbapi-0.2.0/tests/test_cursor_closed_after_connection_gc_pep249.py +116 -0
  231. dqlite_dbapi-0.2.0/tests/test_cursor_closed_state_pep249.py +79 -0
  232. dqlite_dbapi-0.2.0/tests/test_cursor_completed_iterations_resets_on_single_execute.py +100 -0
  233. dqlite_dbapi-0.2.0/tests/test_cursor_connection_partial_init_attributeerror.py +52 -0
  234. dqlite_dbapi-0.2.0/tests/test_cursor_connection_property_after_gc_pep249.py +60 -0
  235. dqlite_dbapi-0.2.0/tests/test_cursor_execute_non_str_operation.py +45 -0
  236. dqlite_dbapi-0.2.0/tests/test_cursor_execute_state_reset_on_failure.py +411 -0
  237. dqlite_dbapi-0.2.0/tests/test_cursor_executemany_non_str_operation.py +80 -0
  238. dqlite_dbapi-0.2.0/tests/test_cursor_executemany_outer_shape_check.py +82 -0
  239. dqlite_dbapi-0.2.0/tests/test_cursor_factory_kwarg_rejected.py +56 -0
  240. dqlite_dbapi-0.2.0/tests/test_cursor_force_close_race.py +127 -0
  241. dqlite_dbapi-0.2.0/tests/test_cursor_iter_clears_messages_pep249.py +96 -0
  242. dqlite_dbapi-0.2.0/tests/test_cursor_iterator_reset.py +104 -0
  243. dqlite_dbapi-0.2.0/tests/test_cursor_kwargs_rejection_precedence.py +76 -0
  244. dqlite_dbapi-0.2.0/tests/test_cursor_next_row_factory_raise_replays_row.py +142 -0
  245. dqlite_dbapi-0.2.0/tests/test_cursor_per_row_types.py +177 -0
  246. dqlite_dbapi-0.2.0/tests/test_cursor_post_close_property_reads.py +121 -0
  247. dqlite_dbapi-0.2.0/tests/test_cursor_protocol_check.py +53 -0
  248. dqlite_dbapi-0.2.0/tests/test_cursor_rejects_generator_params.py +53 -0
  249. dqlite_dbapi-0.2.0/tests/test_cursor_repr_with_gced_parent_connection.py +85 -0
  250. dqlite_dbapi-0.2.0/tests/test_cursor_row_factory_rejecting_setter.py +104 -0
  251. dqlite_dbapi-0.2.0/tests/test_cursor_row_factory_subclass_inheritance.py +90 -0
  252. dqlite_dbapi-0.2.0/tests/test_cursor_row_index_reset.py +89 -0
  253. dqlite_dbapi-0.2.0/tests/test_cursor_rowcount_after_ddl_stdlib_parity.py +269 -0
  254. dqlite_dbapi-0.2.0/tests/test_cursor_rownumber_affinity.py +63 -0
  255. dqlite_dbapi-0.2.0/tests/test_cursor_rownumber_post_close.py +80 -0
  256. dqlite_dbapi-0.2.0/tests/test_cursor_scroll_mode_validation.py +157 -0
  257. dqlite_dbapi-0.2.0/tests/test_cursor_secondary_methods_positional_only.py +140 -0
  258. dqlite_dbapi-0.2.0/tests/test_cursor_setinputsizes_affinity_precedes_shape.py +168 -0
  259. dqlite_dbapi-0.2.0/tests/test_cursor_setinputsizes_setoutputsize_closed_state_precedes_validation.py +104 -0
  260. dqlite_dbapi-0.2.0/tests/test_cursor_setters_clear_messages_pep249.py +56 -0
  261. dqlite_dbapi-0.2.0/tests/test_cursor_stop_iteration_contract.py +90 -0
  262. dqlite_dbapi-0.2.0/tests/test_cursor_uses_run_protocol.py +63 -0
  263. dqlite_dbapi-0.2.0/tests/test_date_widens_to_datetime.py +34 -0
  264. dqlite_dbapi-0.2.0/tests/test_datetime_from_iso8601_docstring_two_step_fallback.py +47 -0
  265. dqlite_dbapi-0.2.0/tests/test_datetime_time_round_trip.py +54 -0
  266. dqlite_dbapi-0.2.0/tests/test_dbapi_commit_deferred_fk.py +39 -0
  267. dqlite_dbapi-0.2.0/tests/test_dbapi_default_timeouts_use_client_constants.py +57 -0
  268. dqlite_dbapi-0.2.0/tests/test_dbapi_exception_message_arg_cap.py +115 -0
  269. dqlite_dbapi-0.2.0/tests/test_dbapi_exception_raw_message_cap.py +77 -0
  270. dqlite_dbapi-0.2.0/tests/test_dbapi_fork_guard_cluster.py +246 -0
  271. dqlite_dbapi-0.2.0/tests/test_dbapi_hardening.py +548 -0
  272. dqlite_dbapi-0.2.0/tests/test_dbapi_type_hash_consistency.py +77 -0
  273. dqlite_dbapi-0.2.0/tests/test_description_null_first_row_resolves.py +125 -0
  274. dqlite_dbapi-0.2.0/tests/test_description_null_type_code_to_none.py +101 -0
  275. dqlite_dbapi-0.2.0/tests/test_description_strict_type_codes.py +59 -0
  276. dqlite_dbapi-0.2.0/tests/test_description_tuple_public_alias.py +44 -0
  277. dqlite_dbapi-0.2.0/tests/test_diagnostic_messages.py +140 -0
  278. dqlite_dbapi-0.2.0/tests/test_dial_attempt_timeout_propagation.py +117 -0
  279. dqlite_dbapi-0.2.0/tests/test_dial_func_propagation.py +82 -0
  280. dqlite_dbapi-0.2.0/tests/test_dial_func_propagation_to_leader_resolve.py +180 -0
  281. dqlite_dbapi-0.2.0/tests/test_doc_class_and_description_drift.py +68 -0
  282. dqlite_dbapi-0.2.0/tests/test_dqlite_namespace_error_classification.py +52 -0
  283. dqlite_dbapi-0.2.0/tests/test_ensure_loop_fast_path_snapshot_dcl_defence.py +100 -0
  284. dqlite_dbapi-0.2.0/tests/test_ensure_loop_post_fork_pid_guard.py +133 -0
  285. dqlite_dbapi-0.2.0/tests/test_error_code_mapping.py +120 -0
  286. dqlite_dbapi-0.2.0/tests/test_error_pickle_cause_either_or.py +50 -0
  287. dqlite_dbapi-0.2.0/tests/test_error_pickle_preserves_raw_message.py +78 -0
  288. dqlite_dbapi-0.2.0/tests/test_exception_coded_mixin.py +112 -0
  289. dqlite_dbapi-0.2.0/tests/test_exception_mapping.py +79 -0
  290. dqlite_dbapi-0.2.0/tests/test_exception_repr_code.py +55 -0
  291. dqlite_dbapi-0.2.0/tests/test_exception_wrapping.py +131 -0
  292. dqlite_dbapi-0.2.0/tests/test_execute_caller_param_shape_preserves_prior_state.py +111 -0
  293. dqlite_dbapi-0.2.0/tests/test_execute_executemany_positional_only.py +77 -0
  294. dqlite_dbapi-0.2.0/tests/test_execute_multi_statement_rejection.py +114 -0
  295. dqlite_dbapi-0.2.0/tests/test_execute_reject_scrubs_prior_result_state.py +212 -0
  296. dqlite_dbapi-0.2.0/tests/test_executemany_accumulator_semantics.py +110 -0
  297. dqlite_dbapi-0.2.0/tests/test_executemany_basecaught_state_reset.py +230 -0
  298. dqlite_dbapi-0.2.0/tests/test_executemany_cancel_restores_pre_batch_lastrowid.py +162 -0
  299. dqlite_dbapi-0.2.0/tests/test_executemany_cancellederror_restores_pre_batch_lastrowid.py +104 -0
  300. dqlite_dbapi-0.2.0/tests/test_executemany_classifier_resets_state_first.py +120 -0
  301. dqlite_dbapi-0.2.0/tests/test_executemany_classify_caller_sql_hoisted.py +61 -0
  302. dqlite_dbapi-0.2.0/tests/test_executemany_completed_iterations_snapshot_conditional_restore.py +160 -0
  303. dqlite_dbapi-0.2.0/tests/test_executemany_dict_subclass_accepted.py +80 -0
  304. dqlite_dbapi-0.2.0/tests/test_executemany_empty.py +112 -0
  305. dqlite_dbapi-0.2.0/tests/test_executemany_empty_batch_preserves_lastrowid.py +65 -0
  306. dqlite_dbapi-0.2.0/tests/test_executemany_none_seq_rejected.py +78 -0
  307. dqlite_dbapi-0.2.0/tests/test_executemany_per_iter_structural_rejects.py +122 -0
  308. dqlite_dbapi-0.2.0/tests/test_executemany_pragma_with_leading_comment.py +75 -0
  309. dqlite_dbapi-0.2.0/tests/test_executemany_reject_scrubs_prior_result_state.py +265 -0
  310. dqlite_dbapi-0.2.0/tests/test_executemany_rejects_pure_queries.py +103 -0
  311. dqlite_dbapi-0.2.0/tests/test_executemany_rejects_transaction_verbs.py +288 -0
  312. dqlite_dbapi-0.2.0/tests/test_executemany_returning_cap.py +69 -0
  313. dqlite_dbapi-0.2.0/tests/test_exit_logs_rollback_failure.py +102 -0
  314. dqlite_dbapi-0.2.0/tests/test_fetchall_row_factory_raise_replay.py +147 -0
  315. dqlite_dbapi-0.2.0/tests/test_fetchmany_edges.py +73 -0
  316. dqlite_dbapi-0.2.0/tests/test_fetchmany_negative_size_rejected.py +91 -0
  317. dqlite_dbapi-0.2.0/tests/test_fetchmany_non_int_validation.py +78 -0
  318. dqlite_dbapi-0.2.0/tests/test_fetchmany_one_clear_per_call_pep249.py +111 -0
  319. dqlite_dbapi-0.2.0/tests/test_fetchmany_row_factory_raise_replay.py +128 -0
  320. dqlite_dbapi-0.2.0/tests/test_fetchmany_zero_docstring_stdlib_divergence.py +60 -0
  321. dqlite_dbapi-0.2.0/tests/test_finalizer_strict_warnings.py +53 -0
  322. dqlite_dbapi-0.2.0/tests/test_force_close_cancel_and_observe_narrow_suppress.py +35 -0
  323. dqlite_dbapi-0.2.0/tests/test_force_close_observe_ast_pin.py +82 -0
  324. dqlite_dbapi-0.2.0/tests/test_force_close_observe_callback_coverage.py +71 -0
  325. dqlite_dbapi-0.2.0/tests/test_force_close_transport_cleanup_tail_unconditional.py +103 -0
  326. dqlite_dbapi-0.2.0/tests/test_force_close_transport_clears_messages.py +95 -0
  327. dqlite_dbapi-0.2.0/tests/test_force_close_transport_cross_thread_cancel_threadsafe.py +129 -0
  328. dqlite_dbapi-0.2.0/tests/test_force_close_transport_cursor_cascade_threadsafe.py +161 -0
  329. dqlite_dbapi-0.2.0/tests/test_force_close_transport_defensive_arms.py +123 -0
  330. dqlite_dbapi-0.2.0/tests/test_force_close_transport_disarms_inner_finalizer.py +85 -0
  331. dqlite_dbapi-0.2.0/tests/test_force_close_transport_pending_drain_resnapshot.py +143 -0
  332. dqlite_dbapi-0.2.0/tests/test_force_close_transport_writer_cross_thread.py +130 -0
  333. dqlite_dbapi-0.2.0/tests/test_fork_diagnostic_includes_pids.py +48 -0
  334. dqlite_dbapi-0.2.0/tests/test_fromticks_tz_policy.py +44 -0
  335. dqlite_dbapi-0.2.0/tests/test_gc_cleanup.py +55 -0
  336. dqlite_dbapi-0.2.0/tests/test_get_resolve_leader_cluster_no_loop_raises_runtime_error.py +37 -0
  337. dqlite_dbapi-0.2.0/tests/test_in_transaction_after_close.py +113 -0
  338. dqlite_dbapi-0.2.0/tests/test_in_transaction_closed_divergence_doc.py +55 -0
  339. dqlite_dbapi-0.2.0/tests/test_in_transaction_closed_foreign_thread.py +107 -0
  340. dqlite_dbapi-0.2.0/tests/test_in_transaction_property.py +39 -0
  341. dqlite_dbapi-0.2.0/tests/test_init_docstring_fromticks_three_impls.py +64 -0
  342. dqlite_dbapi-0.2.0/tests/test_is_multi_statement_walker.py +61 -0
  343. dqlite_dbapi-0.2.0/tests/test_is_no_transaction_error.py +162 -0
  344. dqlite_dbapi-0.2.0/tests/test_is_row_returning_multiline_whitespace.py +112 -0
  345. dqlite_dbapi-0.2.0/tests/test_is_row_returning_sql_noise.py +76 -0
  346. dqlite_dbapi-0.2.0/tests/test_iso8601_aware_microseconds_round_trip.py +72 -0
  347. dqlite_dbapi-0.2.0/tests/test_iso8601_decoder_leap_seconds_rejected.py +39 -0
  348. dqlite_dbapi-0.2.0/tests/test_iso8601_decoder_subsecond_truncation.py +92 -0
  349. dqlite_dbapi-0.2.0/tests/test_iso8601_encoder.py +471 -0
  350. dqlite_dbapi-0.2.0/tests/test_iso8601_encoder_fold.py +73 -0
  351. dqlite_dbapi-0.2.0/tests/test_iso8601_encoder_utcoffset_exception_wrap.py +133 -0
  352. dqlite_dbapi-0.2.0/tests/test_iso8601_parse_error_truncation.py +67 -0
  353. dqlite_dbapi-0.2.0/tests/test_iso8601_tzinfo_exception_classes_wrapped.py +83 -0
  354. dqlite_dbapi-0.2.0/tests/test_isolation_level_stdlib_accept_set.py +83 -0
  355. dqlite_dbapi-0.2.0/tests/test_iterdump_stub_signature_3_13_filter_kwarg.py +69 -0
  356. dqlite_dbapi-0.2.0/tests/test_lastrowid_docstring_stdlib_parity_claim.py +66 -0
  357. dqlite_dbapi-0.2.0/tests/test_lastrowid_insert_only.py +72 -0
  358. dqlite_dbapi-0.2.0/tests/test_lastrowid_signed_int64_cast.py +59 -0
  359. dqlite_dbapi-0.2.0/tests/test_leader_error_codes_identity.py +45 -0
  360. dqlite_dbapi-0.2.0/tests/test_leader_redirect_on_connect.py +279 -0
  361. dqlite_dbapi-0.2.0/tests/test_legacy_transaction_control_constant.py +44 -0
  362. dqlite_dbapi-0.2.0/tests/test_max_continuation_frames_upper_bound.py +65 -0
  363. dqlite_dbapi-0.2.0/tests/test_max_message_size_round_trip.py +82 -0
  364. dqlite_dbapi-0.2.0/tests/test_max_total_rows_plumbing.py +70 -0
  365. dqlite_dbapi-0.2.0/tests/test_max_total_rows_validation.py +56 -0
  366. dqlite_dbapi-0.2.0/tests/test_messages_attribute.py +34 -0
  367. dqlite_dbapi-0.2.0/tests/test_messages_clear_on_ctxmgr_entry.py +84 -0
  368. dqlite_dbapi-0.2.0/tests/test_module_attributes.py +243 -0
  369. dqlite_dbapi-0.2.0/tests/test_module_level_stubs.py +107 -0
  370. dqlite_dbapi-0.2.0/tests/test_nextset_noreturn_annotation.py +43 -0
  371. dqlite_dbapi-0.2.0/tests/test_no_transaction_substrings_source_of_truth.py +31 -0
  372. dqlite_dbapi-0.2.0/tests/test_no_tx_primary_codes_invariant.py +65 -0
  373. dqlite_dbapi-0.2.0/tests/test_op_lock_bounded_acquire.py +87 -0
  374. dqlite_dbapi-0.2.0/tests/test_operational_error_classification.py +76 -0
  375. dqlite_dbapi-0.2.0/tests/test_operational_error_raw_message.py +81 -0
  376. dqlite_dbapi-0.2.0/tests/test_param_conversion.py +71 -0
  377. dqlite_dbapi-0.2.0/tests/test_param_validation.py +48 -0
  378. dqlite_dbapi-0.2.0/tests/test_paramstyle_qmark_only_pin.py +75 -0
  379. dqlite_dbapi-0.2.0/tests/test_parse_decltypes_colnames_constants.py +65 -0
  380. dqlite_dbapi-0.2.0/tests/test_pep249_database_error_routing.py +55 -0
  381. dqlite_dbapi-0.2.0/tests/test_pep249_exception_aliases.py +80 -0
  382. dqlite_dbapi-0.2.0/tests/test_pep249_messages_clearing.py +256 -0
  383. dqlite_dbapi-0.2.0/tests/test_pep249_messages_independence.py +88 -0
  384. dqlite_dbapi-0.2.0/tests/test_pep249_misc_pins.py +181 -0
  385. dqlite_dbapi-0.2.0/tests/test_pep249_stub_hasattr_divergence.py +103 -0
  386. dqlite_dbapi-0.2.0/tests/test_pep249_type_constructors_vs_singletons.py +81 -0
  387. dqlite_dbapi-0.2.0/tests/test_pep249_type_constructors_wrap_valueerror.py +49 -0
  388. dqlite_dbapi-0.2.0/tests/test_pep249_type_objects_final_annotated.py +42 -0
  389. dqlite_dbapi-0.2.0/tests/test_pickle_guard.py +80 -0
  390. dqlite_dbapi-0.2.0/tests/test_pragma_write_description_none.py +60 -0
  391. dqlite_dbapi-0.2.0/tests/test_prepare_protocol_in_types_all.py +38 -0
  392. dqlite_dbapi-0.2.0/tests/test_property_round_trip_and_fork_guard.py +255 -0
  393. dqlite_dbapi-0.2.0/tests/test_property_setters_do_not_mirror_to_inner_async_conn.py +107 -0
  394. dqlite_dbapi-0.2.0/tests/test_protocol_serialization.py +99 -0
  395. dqlite_dbapi-0.2.0/tests/test_pyproject_dependency_graph.py +52 -0
  396. dqlite_dbapi-0.2.0/tests/test_readme_truthfulness_pins.py +73 -0
  397. dqlite_dbapi-0.2.0/tests/test_register_adapter_conform_fallback.py +162 -0
  398. dqlite_dbapi-0.2.0/tests/test_register_adapter_global_sync_aio_symmetry.py +73 -0
  399. dqlite_dbapi-0.2.0/tests/test_repr.py +66 -0
  400. dqlite_dbapi-0.2.0/tests/test_repr_sanitizes_address.py +162 -0
  401. dqlite_dbapi-0.2.0/tests/test_resolve_leader_cache_dial_func_id_recycle.py +178 -0
  402. dqlite_dbapi-0.2.0/tests/test_resolve_leader_cache_lock_atfork_hook.py +73 -0
  403. dqlite_dbapi-0.2.0/tests/test_resolve_leader_cluster_cache.py +124 -0
  404. dqlite_dbapi-0.2.0/tests/test_resolve_leader_cross_loop_isolation.py +233 -0
  405. dqlite_dbapi-0.2.0/tests/test_row_factory_annotation.py +67 -0
  406. dqlite_dbapi-0.2.0/tests/test_row_factory_sqlite3_row_surfaces_dataerror.py +94 -0
  407. dqlite_dbapi-0.2.0/tests/test_rowid_number_overlap_documented.py +43 -0
  408. dqlite_dbapi-0.2.0/tests/test_rownumber_closed_cursor_returns_none_documented.py +87 -0
  409. dqlite_dbapi-0.2.0/tests/test_run_sync_ensure_loop_raise_closes_coroutine.py +105 -0
  410. dqlite_dbapi-0.2.0/tests/test_run_sync_keyboard_interrupt.py +373 -0
  411. dqlite_dbapi-0.2.0/tests/test_run_sync_keyboard_interrupt_cleanup_paths.py +162 -0
  412. dqlite_dbapi-0.2.0/tests/test_run_sync_ki_post_schedule_pre_await.py +122 -0
  413. dqlite_dbapi-0.2.0/tests/test_run_sync_ki_race_recovery.py +198 -0
  414. dqlite_dbapi-0.2.0/tests/test_run_sync_loop_closed_during_schedule.py +143 -0
  415. dqlite_dbapi-0.2.0/tests/test_run_sync_other_runtimeerror_propagates.py +113 -0
  416. dqlite_dbapi-0.2.0/tests/test_run_sync_timeout.py +412 -0
  417. dqlite_dbapi-0.2.0/tests/test_run_sync_timeout_recovery_preserves_conn.py +111 -0
  418. dqlite_dbapi-0.2.0/tests/test_secondary_methods_clear_messages_before_thread_check.py +99 -0
  419. dqlite_dbapi-0.2.0/tests/test_setinputsizes_accepts_none.py +60 -0
  420. dqlite_dbapi-0.2.0/tests/test_setinputsizes_clears_messages_per_pep249.py +55 -0
  421. dqlite_dbapi-0.2.0/tests/test_setinputsizes_non_sequence_iterable.py +87 -0
  422. dqlite_dbapi-0.2.0/tests/test_setinputsizes_sequence_acceptance.py +80 -0
  423. dqlite_dbapi-0.2.0/tests/test_setinputsizes_short_circuit_on_closed_connection.py +112 -0
  424. dqlite_dbapi-0.2.0/tests/test_setoutputsize_accepts_none.py +61 -0
  425. dqlite_dbapi-0.2.0/tests/test_setter_closed_state_precedes_thread.py +78 -0
  426. dqlite_dbapi-0.2.0/tests/test_sqlite_errorname_property.py +96 -0
  427. dqlite_dbapi-0.2.0/tests/test_sqlite_version_not_public_attribute.py +52 -0
  428. dqlite_dbapi-0.2.0/tests/test_strip_leading_comments_bom.py +70 -0
  429. dqlite_dbapi-0.2.0/tests/test_strip_leading_comments_parity.py +96 -0
  430. dqlite_dbapi-0.2.0/tests/test_strip_sql_noise_backtick_identifier.py +73 -0
  431. dqlite_dbapi-0.2.0/tests/test_stub_signatures_normalize_to_dbapi_error.py +161 -0
  432. dqlite_dbapi-0.2.0/tests/test_submodule_all.py +39 -0
  433. dqlite_dbapi-0.2.0/tests/test_sync_commit_rollback_closed_precedes_thread.py +47 -0
  434. dqlite_dbapi-0.2.0/tests/test_sync_commit_rollback_inside_ctxmgr_guard.py +125 -0
  435. dqlite_dbapi-0.2.0/tests/test_sync_commit_rollback_messages_under_lock.py +78 -0
  436. dqlite_dbapi-0.2.0/tests/test_sync_commit_rollback_retry_after_invalidate.py +87 -0
  437. dqlite_dbapi-0.2.0/tests/test_sync_connection_execute_shortcut.py +67 -0
  438. dqlite_dbapi-0.2.0/tests/test_sync_connection_executemany_shortcut.py +43 -0
  439. dqlite_dbapi-0.2.0/tests/test_sync_cursor_post_add_closed_recheck.py +151 -0
  440. dqlite_dbapi-0.2.0/tests/test_sync_exit_signal_mid_rollback.py +75 -0
  441. dqlite_dbapi-0.2.0/tests/test_sync_fetchmany_cancel_atomic.py +84 -0
  442. dqlite_dbapi-0.2.0/tests/test_sync_force_close_transport.py +192 -0
  443. dqlite_dbapi-0.2.0/tests/test_sync_force_close_transport_pending_drain_reap.py +185 -0
  444. dqlite_dbapi-0.2.0/tests/test_sync_invalidated_property.py +59 -0
  445. dqlite_dbapi-0.2.0/tests/test_sync_run_sync_multiphase_timeout.py +127 -0
  446. dqlite_dbapi-0.2.0/tests/test_sync_transaction_owner_token_bookkeeping.py +170 -0
  447. dqlite_dbapi-0.2.0/tests/test_thread_safety_enforcement.py +359 -0
  448. dqlite_dbapi-0.2.0/tests/test_timeout_validation.py +74 -0
  449. dqlite_dbapi-0.2.0/tests/test_top_level_logger_has_null_handler.py +28 -0
  450. dqlite_dbapi-0.2.0/tests/test_total_changes_hasattr_safe.py +62 -0
  451. dqlite_dbapi-0.2.0/tests/test_transitive_wire_import.py +52 -0
  452. dqlite_dbapi-0.2.0/tests/test_type_coercion_documented_asymmetries.py +183 -0
  453. dqlite_dbapi-0.2.0/tests/test_types_dbapi_compare.py +263 -0
  454. dqlite_dbapi-0.2.0/tests/test_types_validator_branches.py +125 -0
  455. dqlite_dbapi-0.2.0/tests/test_unregister_adapter.py +50 -0
  456. dqlite_dbapi-0.2.0/tests/test_unregister_adapter_rejects_built_in_datetime_types.py +87 -0
  457. dqlite_dbapi-0.2.0/tests/test_unsupported_method_stubs.py +403 -0
  458. dqlite_dbapi-0.2.0/tests/test_validate_positive_int_pep249_wrap.py +103 -0
  459. dqlite_dbapi-0.2.0/tests/test_validate_ticks.py +86 -0
  460. dqlite_dbapi-0.2.0/tests/test_validate_ticks_and_unixtime_reject_bool_and_non_numeric.py +79 -0
  461. dqlite_dbapi-0.2.0/tests/test_version.py +13 -0
  462. dqlite_dbapi-0.2.0/tests/test_version_export.py +19 -0
  463. dqlite_dbapi-0.2.0/uv.lock +360 -0
  464. dqlite_dbapi-0.1.2/PKG-INFO +0 -82
  465. dqlite_dbapi-0.1.2/README.md +0 -51
  466. dqlite_dbapi-0.1.2/pyproject.toml +0 -63
  467. dqlite_dbapi-0.1.2/src/dqlitedbapi/__init__.py +0 -98
  468. dqlite_dbapi-0.1.2/src/dqlitedbapi/aio/__init__.py +0 -43
  469. dqlite_dbapi-0.1.2/src/dqlitedbapi/aio/connection.py +0 -86
  470. dqlite_dbapi-0.1.2/src/dqlitedbapi/aio/cursor.py +0 -166
  471. dqlite_dbapi-0.1.2/src/dqlitedbapi/connection.py +0 -128
  472. dqlite_dbapi-0.1.2/src/dqlitedbapi/cursor.py +0 -177
  473. dqlite_dbapi-0.1.2/src/dqlitedbapi/exceptions.py +0 -61
  474. dqlite_dbapi-0.1.2/src/dqlitedbapi/types.py +0 -64
  475. dqlite_dbapi-0.1.2/tests/conftest.py +0 -1
  476. dqlite_dbapi-0.1.2/tests/integration/test_returning.py +0 -64
  477. dqlite_dbapi-0.1.2/tests/test_cursor.py +0 -88
  478. dqlite_dbapi-0.1.2/tests/test_module_attributes.py +0 -92
  479. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/.github/workflows/publish-to-pypi.yml +0 -0
  480. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/.gitignore +0 -0
  481. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/LICENSE.md +0 -0
  482. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/src/dqlitedbapi/py.typed +0 -0
  483. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/tests/__init__.py +0 -0
  484. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/tests/integration/__init__.py +0 -0
  485. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/tests/integration/conftest.py +0 -0
  486. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.2.0}/tests/test_exceptions.py +0 -0
@@ -8,15 +8,14 @@
8
8
 
9
9
  ## Setup
10
10
 
11
- Start by also cloning [dqlite-wire](https://github.com/letsdiscodev/python-dqlite-wire) and [dqlite-client](https://github.com/letsdiscodev/python-dqlite-client).
11
+ Clone [dqlite-wire](https://github.com/letsdiscodev/python-dqlite-wire) and [dqlite-client](https://github.com/letsdiscodev/python-dqlite-client) alongside this checkout; `[tool.uv.sources]` in `pyproject.toml` points the sibling packages at `../python-dqlite-wire` and `../python-dqlite-client`, so `uv sync` picks up in-tree changes automatically.
12
12
 
13
13
  ```bash
14
14
  # Install uv (if not already installed)
15
15
  curl -LsSf https://astral.sh/uv/install.sh | sh
16
16
 
17
17
  # Create virtual environment and install dependencies
18
- uv venv --python 3.13
19
- uv pip install -e "../python-dqlite-wire" -e "../python-dqlite-client" -e ".[dev]"
18
+ uv sync --extra dev
20
19
  ```
21
20
 
22
21
  ## Development Tools
@@ -0,0 +1,369 @@
1
+ Metadata-Version: 2.4
2
+ Name: dqlite-dbapi
3
+ Version: 0.2.0
4
+ Summary: PEP 249 (DB-API 2.0) compliant interface for dqlite
5
+ Project-URL: Homepage, https://github.com/letsdiscodev/python-dqlite-dbapi
6
+ Project-URL: Repository, https://github.com/letsdiscodev/python-dqlite-dbapi
7
+ Project-URL: Issues, https://github.com/letsdiscodev/python-dqlite-dbapi/issues
8
+ Author-email: Antoine Leclair <antoineleclair@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE.md
11
+ Keywords: database,dbapi,distributed,dqlite,pep249,sqlite
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Database
21
+ Classifier: Topic :: Database :: Database Engines/Servers
22
+ Classifier: Topic :: Database :: Front-Ends
23
+ Classifier: Typing :: Typed
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
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.0; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.4; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # dqlite-dbapi
36
+
37
+ PEP 249 compliant interface for [dqlite](https://dqlite.io/).
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install dqlite-dbapi
43
+ ```
44
+
45
+ ## Sync Usage
46
+
47
+ ```python
48
+ import dqlitedbapi
49
+
50
+ conn = dqlitedbapi.connect("localhost:9001")
51
+ cursor = conn.cursor()
52
+ cursor.execute("SELECT 1")
53
+ print(cursor.fetchone())
54
+ conn.close()
55
+ ```
56
+
57
+ ## Async Usage
58
+
59
+ ```python
60
+ import asyncio
61
+ from dqlitedbapi.aio import aconnect
62
+
63
+ async def main():
64
+ conn = await aconnect("localhost:9001")
65
+ cursor = conn.cursor()
66
+ await cursor.execute("SELECT 1")
67
+ print(await cursor.fetchone())
68
+ await conn.close()
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## PEP 249 Compliance
74
+
75
+ - `apilevel = "2.0"`
76
+ - `threadsafety = 1`
77
+ - `paramstyle = "qmark"`
78
+
79
+ ## Transactions
80
+
81
+ `dqlite-dbapi` does **not** issue implicit `BEGIN` before DML — each
82
+ statement runs in the underlying SQLite engine's autocommit mode unless
83
+ the caller has explicitly opened a transaction. This deviates from
84
+ PEP 249 §6's prescribed implicit-transaction model and from stdlib
85
+ `sqlite3` / `psycopg`, both of which auto-BEGIN on the first DML; users
86
+ porting from those drivers will see different behaviour and must add
87
+ explicit `BEGIN` calls (see below) to recover atomic multi-statement
88
+ semantics. The dqlite C and Go reference clients have the same opt-in
89
+ contract; this driver matches them rather than stdlib.
90
+
91
+ If you use SQLAlchemy via `sqlalchemy-dqlite`, the dialect emits
92
+ `BEGIN` for every `engine.begin()` / `connection.begin()` block — no
93
+ explicit `BEGIN` needed; the autocommit-by-default gap below applies
94
+ only to direct dbapi users.
95
+
96
+ Every write still goes through Raft consensus and every read is
97
+ serializable; **isolation is always SERIALIZABLE**, but transaction
98
+ *grouping* is opt-in.
99
+
100
+ To group statements into a transaction, issue an explicit `BEGIN`
101
+ through a cursor (or use `dqliteclient`'s `transaction()` async
102
+ context manager from the layer below):
103
+
104
+ ```python
105
+ cur = conn.cursor()
106
+ cur.execute("BEGIN")
107
+ cur.execute("INSERT INTO t VALUES (?)", (1,))
108
+ cur.execute("INSERT INTO t VALUES (?)", (2,))
109
+ conn.commit() # COMMIT
110
+ ```
111
+
112
+ The bare `BEGIN` SQL is the SQLite default (`BEGIN DEFERRED`). dqlite's
113
+ Raft FSM serializes the transaction across the cluster regardless of
114
+ the `DEFERRED` / `IMMEDIATE` / `EXCLUSIVE` qualifier, so the qualifier
115
+ does not change isolation semantics — only the lock-acquisition timing
116
+ on the leader. The `Connection.isolation_level` attribute exists for
117
+ pre-3.12 stdlib parity: the getter returns `None`, the setter accepts
118
+ only `None` (no-op) and rejects every other value (`""`, `"DEFERRED"`,
119
+ `"IMMEDIATE"`, `"EXCLUSIVE"`, `"SERIALIZABLE"`, `"AUTOCOMMIT"`, etc.)
120
+ with `NotSupportedError`. dqlite is autocommit-by-default at the dbapi
121
+ layer; explicit transactions are managed via `conn.commit()` /
122
+ `conn.rollback()` per PEP 249. The SQLAlchemy dialect rejects
123
+ `AUTOCOMMIT` on the same grounds.
124
+
125
+ Connection-level `commit()` / `rollback()` semantics:
126
+
127
+ - Calling `commit()` / `rollback()` before any query has run is a silent
128
+ no-op (preserves the "no spurious connect" contract — we don't open
129
+ a TCP connection just to send COMMIT).
130
+ - Calling `commit()` / `rollback()` with no active transaction
131
+ (e.g. right after a DDL statement) is also silently successful, matching
132
+ stdlib `sqlite3` semantics.
133
+ - The context-manager exit (`with conn: ...`) commits on clean exit and
134
+ attempts rollback on exception. **Commit failures propagate** —
135
+ silent swallowing was a footgun that could hide data loss.
136
+ - Operational caveat: a leader-flip during COMMIT raises
137
+ `OperationalError` with a code in `dqlitewire.LEADER_ERROR_CODES`. The
138
+ write may or may not have been persisted (Raft may already have
139
+ replicated the commit log entry before the flip). Use idempotent DML
140
+ (`INSERT OR REPLACE`, `UPDATE` on a unique key) or an out-of-band
141
+ state-check before retrying.
142
+
143
+ ## Differences from `aiosqlite`
144
+
145
+ This driver is PEP 249-shaped (sync) and exposes an async surface
146
+ under `dqlitedbapi.aio` that mirrors the sync `dqlitedbapi` surface
147
+ plus async-specific `aconnect` / `AsyncConnection` / `AsyncCursor`.
148
+
149
+ The full PEP 249 module attributes (`apilevel`, `threadsafety`,
150
+ `paramstyle`, `sqlite_version`, `sqlite_version_info`), exception
151
+ hierarchy (`Warning`, `Error`, `InterfaceError`, `DatabaseError`,
152
+ `DataError`, `OperationalError`, `IntegrityError`, `InternalError`,
153
+ `ProgrammingError`, `NotSupportedError`), type constructors
154
+ (`Date`, `Time`, `Timestamp`, `DateFromTicks`, `TimeFromTicks`,
155
+ `TimestampFromTicks`, `Binary`), type sentinels (`STRING`, `BINARY`,
156
+ `NUMBER`, `DATETIME`, `ROWID`), the working `register_adapter` /
157
+ `unregister_adapter` (process-global; mirrors stdlib's pre-3.12
158
+ behavior), and stdlib-sqlite3-parity stubs that raise
159
+ `NotSupportedError` (`register_converter`, `complete_statement`,
160
+ `enable_callback_tracebacks`) are all re-exported under
161
+ `dqlitedbapi.aio` so cross-driver code porting from aiosqlite
162
+ imports them from one namespace. One notable deviation:
163
+
164
+ - **`AsyncConnection.__aexit__` does NOT close the connection.** It
165
+ commits / rolls back per the body outcome and leaves the
166
+ connection reusable, matching stdlib `sqlite3.Connection.__exit__`
167
+ and PEP 249 §7. `aiosqlite.Connection.__aexit__` DOES close (closes
168
+ its proxy thread + sqlite3 connection). Cross-driver code porting
169
+ from `aiosqlite` must add an explicit `await conn.close()` (or
170
+ switch to a pool) for eager-close semantics.
171
+
172
+ ```python
173
+ # aiosqlite-style (closes on aexit):
174
+ async with aiosqlite.connect(":memory:") as conn:
175
+ ...
176
+
177
+ # dqlitedbapi.aio-style (does NOT close on aexit, matches stdlib sqlite3):
178
+ async with await dqlitedbapi.aio.aconnect(...) as conn:
179
+ ...
180
+ await conn.close() # explicit
181
+ ```
182
+
183
+ ## Layering (pool ownership)
184
+
185
+ Each `dqlitedbapi.Connection` (sync or async) owns exactly one
186
+ underlying `dqliteclient.DqliteConnection`. The dbapi layer does NOT
187
+ itself pool client connections — pooling lives one layer up:
188
+
189
+ - **SQLAlchemy users**: SA's `QueuePool` (and async siblings) over
190
+ dbapi `Connection` objects is the production pool.
191
+ - **Direct dbapi users**: a custom pool over dbapi `Connection`
192
+ objects is the supported pattern.
193
+ - `dqliteclient.ConnectionPool` is for direct-client usage and is
194
+ unused by dqlitedbapi.
195
+
196
+ A dbapi `Connection.close()` always closes the underlying client
197
+ transport — not "return to a pool" — because the dbapi never
198
+ borrowed from one.
199
+
200
+ ## Limitations vs. stdlib `sqlite3`
201
+
202
+ - **Multi-statement SQL is rejected.** `cursor.execute("SELECT 1;
203
+ SELECT 2;")` raises `ProgrammingError("You can only execute one
204
+ statement at a time.")` (stdlib's wording, but client-side, before
205
+ any wire round-trip). Split into separate `execute()` calls; there
206
+ is no `executescript` (it raises `NotSupportedError`).
207
+ - **`paramstyle = "qmark"` only.** stdlib accepts both `?` (qmark)
208
+ AND `:name` (named) within a single connection; this driver
209
+ advertises only `qmark` and rejects mappings with `ProgrammingError`.
210
+ - **Autocommit-by-default at the server.** Opposite of stdlib
211
+ `sqlite3`'s implicit-transaction model — see Transactions above.
212
+ - **`Cursor.rowcount = len(rows)` after SELECT.** stdlib returns `-1`
213
+ (rowcount is undefined for queries). dqlite knows the full result
214
+ set at execute time, so the literal-PEP-249 reading is honoured.
215
+ `rowcount = -1` is preserved for true non-result paths (PRAGMA
216
+ write, never-executed cursor).
217
+ - **`Cursor.executemany` clears `lastrowid` to `None`.** stdlib leaves
218
+ the prior INSERT's rowid sticky across executemany. The driver
219
+ diverges deliberately (which row's rowid is "the" rowid for a batch
220
+ of N inserts is ambiguous).
221
+ - **`Cursor.close()` scrubs `description` / `lastrowid` / `_rows`.**
222
+ stdlib preserves `description` and `lastrowid` post-close; dqlite
223
+ scrubs to `None` to enforce the closed-cursor "no operation
224
+ performed" surface.
225
+ - **`Cursor.lastrowid` returns `None` after a fresh CREATE TABLE.**
226
+ stdlib `sqlite3` returns `0` for a never-INSERTed cursor; dqlite
227
+ returns `None`. After the first INSERT/REPLACE both drivers agree
228
+ on the sticky value through subsequent UPDATE / DELETE / DDL.
229
+ - **`Cursor.fetchmany(0)` returns `[]`.** stdlib (verified 3.12.3) and
230
+ psycopg3 both treat `size=0` as "fetch arraysize" or "fetch all
231
+ remaining"; dqlite reads PEP 249 literally (size=0 → 0 rows). Pass
232
+ `None` or omit the argument to default to `arraysize`.
233
+ - **`Cursor.fetchmany(negative)` raises `ProgrammingError`.** Stdlib
234
+ `sqlite3.Cursor.fetchmany` on Python 3.13+ rejects negative sizes
235
+ with `ValueError`; older stdlib drained "fetch all remaining". The
236
+ dbapi follows current stdlib but wraps the rejection in
237
+ `ProgrammingError` to keep it in the dbapi.Error hierarchy per
238
+ PEP 249 §7.
239
+ - **`cursor.execute("")` raises `ProgrammingError("empty statement")`.**
240
+ stdlib silently accepts empty / whitespace-only / comment-only SQL
241
+ as a no-op; the driver pre-flight rejects per PEP 249 §7 to surface
242
+ 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.
247
+ - **No `executescript` / `create_function` / `create_aggregate` /
248
+ `create_window_function` / `iterdump` / `backup` / `set_authorizer`
249
+ / `serialize` / `blobopen`.** stdlib-specific APIs that have no
250
+ server-side counterpart in dqlite. Stubs raise `NotSupportedError`.
251
+ - **SERIALIZABLE isolation only.** Every statement is ordered by Raft;
252
+ weaker isolation levels aren't exposed.
253
+ - **PEP 249 type sentinels (`STRING`, `BINARY`, `NUMBER`, `DATETIME`,
254
+ `ROWID`) are unhashable.** Use chained equality against
255
+ `description[i][1]`, NOT set/dict membership:
256
+
257
+ ```python
258
+ type_code = cur.description[i][1]
259
+ if type_code == STRING or type_code == NUMBER: # OK
260
+ ...
261
+ if type_code in {STRING, NUMBER}: # raises TypeError
262
+ ...
263
+ ```
264
+
265
+ The sentinels wrap multiple wire type codes (`NUMBER` covers
266
+ INTEGER+FLOAT+BOOLEAN, `DATETIME` covers DATE+TIMESTAMP+ISO8601),
267
+ so no canonical hash can satisfy the Python hash-eq invariant.
268
+ Stdlib `sqlite3` doesn't export these sentinels at all, so the
269
+ chained-equality form is the cross-driver-portable idiom.
270
+
271
+ - **`Binary(value)` leaks bare `TypeError` on bad input.** `Binary` is
272
+ the stdlib `sqlite3.Binary = memoryview` alias, kept as a direct
273
+ alias so `isinstance(Binary(b), memoryview)` holds for cross-driver
274
+ porting code. Bad input (`Binary("not bytes")`, `Binary(123)`,
275
+ `Binary(None)`) raises bare `TypeError` from the underlying
276
+ `memoryview` constructor, *outside* the `dqlitedbapi.Error`
277
+ hierarchy. The sibling `Date`/`Time`/`Timestamp`/`*FromTicks`
278
+ constructors all wrap as `DataError`; `Binary` deliberately does
279
+ not. Callers who want PEP 249 §7 hierarchy purity for binary input
280
+ should wrap their own `try`/`except (TypeError, ValueError)` and
281
+ re-raise as `dqlitedbapi.DataError`.
282
+
283
+ - **`WITH ... INSERT/UPDATE/DELETE` (CTE-prefixed pure DML) reports
284
+ zero `rowcount` and no `lastrowid`.** The driver dispatches between
285
+ the row-returning and execute paths via a prefix-based heuristic;
286
+ CTE-prefixed pure DML is misclassified as row-returning and the
287
+ server's actual count / id is dropped. Stdlib `sqlite3` handles
288
+ this correctly because it dispatches at the SQLite engine level.
289
+ Workaround: rewrite as plain DML, or use `INSERT ... RETURNING id`
290
+ to get the id back through the row-returning path. (PEP 249
291
+ doesn't mandate `lastrowid` correctness for INSERT-via-CTE.)
292
+
293
+ - **`description[i][1]` is `None` on empty result sets.** PEP 249 §6.1.2
294
+ requires `type_code` to compare equal to one of the Type Objects
295
+ (`STRING` / `NUMBER` / `BINARY` / `DATETIME` / `ROWID`). On an empty
296
+ result (zero rows AND zero per-column type tags — the wire layer
297
+ derives types from the first row's header), the per-column affinity
298
+ is genuinely unrecoverable from the wire; the driver emits `None`
299
+ rather than synthesise a misleading default. Cross-driver
300
+ `type_code == STRING` tests silently fail to match. Callers needing
301
+ column types on empty result sets should issue
302
+ `PRAGMA table_info(<table>)` separately. The sibling NULL-first-row
303
+ case is handled by a row-scan rescue (see `Cursor.description`
304
+ docstring).
305
+
306
+ - **Result sets are fully materialised at `execute()` time.** Stdlib
307
+ `sqlite3` streams rows from the C engine via `Cursor.fetchone`,
308
+ yielding one row per call. dqlite drains every continuation frame
309
+ inside `Cursor.execute()` before returning, materialising the full
310
+ result into the cursor's in-memory `_rows`. The `fetchone` /
311
+ `fetchmany` / `fetchall` surface then iterates the in-memory
312
+ buffer; `Cursor.close()` is purely in-memory and does not send
313
+ `INTERRUPT` to the server. For large queries, set `max_total_rows`
314
+ (forwarded from `connect()`) to cap memory at the wire layer, or
315
+ shape queries with explicit `LIMIT` to control batch size. This
316
+ matches the dqlite wire protocol (continuation frames are
317
+ server-pushed) but diverges from go-dqlite's lazy `Rows.Next`.
318
+
319
+ - **`SQLITE_TOOBIG` maps to `DataError`, not `DatabaseError`.** CPython
320
+ `Modules/_sqlite/util.c::get_exception_class` maps `SQLITE_TOOBIG`
321
+ (code 18, "string or BLOB exceeds size limit") to the generic
322
+ `DatabaseError`. The dqlite dbapi maps it to the more specific
323
+ `DataError` since the error is unambiguously a value-size violation.
324
+ Cross-driver code catching `DatabaseError` keeps working
325
+ (`DataError ⊂ DatabaseError`); code that distinguishes on the
326
+ specific subclass catches more here than against stdlib.
327
+
328
+ ## Cross-version semantic shift: NULL in BOOLEAN/DATETIME columns
329
+
330
+ Upstream dqlite commit `f30fc99` (`query: preserve SQLITE_NULL type
331
+ for NULL values`, 2026-01-25) changed the wire encoding of NULL cells
332
+ in columns declared `BOOLEAN`, `DATE`, `DATETIME`, or `TIMESTAMP`:
333
+
334
+ - **Before** `f30fc99`: a NULL cell was emitted with the column's
335
+ coerced type — `BOOLEAN(0)` (decodes to `False`) or `ISO8601("")`
336
+ (decodes to `""`), indistinguishable from a real `FALSE` / empty
337
+ string.
338
+ - **After** `f30fc99`: a NULL cell is emitted with `SQLITE_NULL` and
339
+ decodes to `None`.
340
+
341
+ Code that does `if cur.fetchone()[0] is None:` against an old-server
342
+ cluster will silently miss NULL rows. After a cluster upgrade past
343
+ `f30fc99`, the same code starts firing where it previously read
344
+ `False` / `""`. There is no driver-level handshake distinguishing the
345
+ two server versions — check your dqlite cluster version before relying
346
+ on `is None` for `BOOLEAN` / `DATETIME` columns.
347
+
348
+ ## Layering
349
+
350
+ Three related packages play different roles:
351
+
352
+ - `dqliteclient.DqliteConnection` — the low-level async wire client.
353
+ Directly speaks the dqlite wire protocol.
354
+ - `dqlitedbapi.Connection` — a sync PEP 249 wrapper built on top, runs a
355
+ dedicated event-loop thread so sync code can use the async client
356
+ transparently.
357
+ - `dqlitedbapi.aio.AsyncConnection` — the PEP 249–shaped async
358
+ counterpart for code already running inside an event loop.
359
+
360
+ Use `dqlitedbapi` (sync or async) unless you specifically need
361
+ wire-level control.
362
+
363
+ ## Development
364
+
365
+ See [DEVELOPMENT.md](DEVELOPMENT.md) for setup and contribution guidelines.
366
+
367
+ ## License
368
+
369
+ MIT