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