sqlalchemy-dqlite 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/PKG-INFO +4 -4
  2. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/pyproject.toml +4 -4
  3. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/__init__.py +1 -1
  4. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/aio.py +198 -45
  5. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/base.py +847 -94
  6. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/provision.py +214 -44
  7. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/requirements.py +274 -18
  8. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/conftest.py +17 -0
  9. sqlalchemy_dqlite-0.2.2/tests/integration/test_concurrent_writers_begin_immediate.py +92 -0
  10. sqlalchemy_dqlite-0.2.2/tests/integration/test_dqlite_session_mode_execution_option.py +284 -0
  11. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_engine_begin_is_atomic.py +12 -5
  12. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_pool_recycle_and_dispose_fan_out.py +24 -6
  13. sqlalchemy_dqlite-0.2.2/tests/integration/test_temp_table_scoping_per_connection.py +69 -0
  14. sqlalchemy_dqlite-0.2.2/tests/test_aio_async_ping_closed_proxy_log.py +63 -0
  15. sqlalchemy_dqlite-0.2.2/tests/test_aio_force_close_transport_cancelled_error.py +67 -0
  16. sqlalchemy_dqlite-0.2.2/tests/test_aio_handle_exception_group_single_child_unwrap.py +76 -0
  17. sqlalchemy_dqlite-0.2.2/tests/test_async_adapted_connection_init_positional_swap_guard.py +76 -0
  18. sqlalchemy_dqlite-0.2.2/tests/test_async_adapter_terminate_handled_exceptions_runtimeerror.py +38 -0
  19. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_dialect_isolation_set.py +11 -8
  20. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_bind_side_cross_type_rejection.py +6 -1
  21. sqlalchemy_dqlite-0.2.2/tests/test_busy_timeout_url_and_connect_args.py +138 -0
  22. sqlalchemy_dqlite-0.2.2/tests/test_check_same_thread_url_and_connect_args.py +177 -0
  23. sqlalchemy_dqlite-0.2.2/tests/test_colspecs_pysqlite_time_inherited_baseline.py +29 -0
  24. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_datetime_bind_widens_date.py +14 -4
  25. sqlalchemy_dqlite-0.2.2/tests/test_ddl_kwarg_dqlite_prefix_rejected.py +67 -0
  26. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_ddl_kwarg_prefix_documented.py +19 -17
  27. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect.py +16 -27
  28. sqlalchemy_dqlite-0.2.2/tests/test_dialect_set_isolation_level_annotation.py +35 -0
  29. sqlalchemy_dqlite-0.2.2/tests/test_do_begin_cursor_close_widened_catch.py +85 -0
  30. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_begin_emits_begin.py +14 -15
  31. sqlalchemy_dqlite-0.2.2/tests/test_do_execute_local_override_pin.py +74 -0
  32. sqlalchemy_dqlite-0.2.2/tests/test_do_recover_twophase_noreturn_annotation.py +41 -0
  33. sqlalchemy_dqlite-0.2.2/tests/test_do_terminate_narrow_catch_propagates_refactor_break.py +85 -0
  34. sqlalchemy_dqlite-0.2.2/tests/test_do_terminate_non_raising_contract_widened.py +131 -0
  35. sqlalchemy_dqlite-0.2.2/tests/test_dqlite_datetime_bind_zero_microseconds.py +69 -0
  36. sqlalchemy_dqlite-0.2.2/tests/test_dqlite_datetime_time_literal_processor_microseconds.py +70 -0
  37. sqlalchemy_dqlite-0.2.2/tests/test_dqlite_datetime_types_reject_pysqlite_kwargs.py +73 -0
  38. sqlalchemy_dqlite-0.2.2/tests/test_dqlite_time_bind_processor_combined_fixes.py +70 -0
  39. sqlalchemy_dqlite-0.2.2/tests/test_dqlite_time_result_processor_preserves_offset.py +66 -0
  40. sqlalchemy_dqlite-0.2.2/tests/test_dqlitetime_bind_processor_returns_none.py +41 -0
  41. sqlalchemy_dqlite-0.2.2/tests/test_dqlitetime_cross_axis_narrow_with_timezone_true.py +49 -0
  42. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_bare_databaseerror_type_identity.py +14 -7
  43. sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_bare_dbe_short_circuits_on_slot_fatal_codes.py +115 -0
  44. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_database_error_substring.py +13 -7
  45. sqlalchemy_dqlite-0.2.2/tests/test_provision_drop_user_tables_and_reap.py +301 -0
  46. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_drop_user_tables_log_sanitized.py +17 -8
  47. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_drop_user_tables_quote_escaping.py +15 -5
  48. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_generate_driver_url.py +16 -0
  49. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_result_processor_warning_one_shot.py +40 -7
  50. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_set_isolation_level_case_insensitive.py +8 -5
  51. sqlalchemy_dqlite-0.2.2/tests/test_set_isolation_level_none_rejected.py +48 -0
  52. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_transport_class_exceptions_consolidation.py +9 -6
  53. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/uv.lock +4 -4
  54. sqlalchemy_dqlite-0.2.0/tests/test_aio_force_close_transport_cancelled_error.py +0 -45
  55. sqlalchemy_dqlite-0.2.0/tests/test_dialect_set_isolation_level_annotation.py +0 -35
  56. sqlalchemy_dqlite-0.2.0/tests/test_do_terminate_narrow_catch_propagates_refactor_break.py +0 -65
  57. sqlalchemy_dqlite-0.2.0/tests/test_dqlitetime_bind_processor_returns_none.py +0 -35
  58. sqlalchemy_dqlite-0.2.0/tests/test_dqlitetime_cross_axis_narrow_with_timezone_true.py +0 -50
  59. sqlalchemy_dqlite-0.2.0/tests/test_provision_drop_user_tables_and_reap.py +0 -208
  60. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/.github/workflows/publish-to-pypi.yml +0 -0
  61. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/.gitignore +0 -0
  62. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/DEVELOPMENT.md +0 -0
  63. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/LICENSE.md +0 -0
  64. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/README.md +0 -0
  65. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/scripts/check-commit-msg.sh +0 -0
  66. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/setup.cfg +0 -0
  67. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/py.typed +0 -0
  68. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/__init__.py +0 -0
  69. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/compliance/conftest.py +0 -0
  70. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/compliance/test_suite.py +0 -0
  71. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/__init__.py +0 -0
  72. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/conftest.py +0 -0
  73. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_cancel_during_commit_invalidates_slot.py +0 -0
  74. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_cancel_inside_engine_begin.py +0 -0
  75. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_concurrent_sessions.py +0 -0
  76. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_context.py +0 -0
  77. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_executemany.py +0 -0
  78. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_savepoint_cancellation_invalidates_slot.py +0 -0
  79. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_bulk_dml_returning.py +0 -0
  80. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_date_tz_drop.py +0 -0
  81. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_dial_func_via_connect_args_reaches_dbapi.py +0 -0
  82. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_engine_events_fire.py +0 -0
  83. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_inspect_async_and_close_parity.py +0 -0
  84. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_isolation_level_pin.py +0 -0
  85. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_json_column.py +0 -0
  86. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_native_boolean.py +0 -0
  87. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_orm_operations.py +0 -0
  88. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_pool_pre_ping_leader_flip.py +0 -0
  89. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_pool_reset_on_return.py +0 -0
  90. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_reflect_column_types.py +0 -0
  91. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_savepoint_basic.py +0 -0
  92. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_savepoint_orm_session_begin_nested.py +0 -0
  93. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_savepoint_quoted_identifiers.py +0 -0
  94. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_time_and_reflection.py +0 -0
  95. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_upsert.py +0 -0
  96. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_2pc_not_supported_error.py +0 -0
  97. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_arraysize_default_and_semantics.py +0 -0
  98. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_arraysize_validation.py +0 -0
  99. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_cursor_iterator_protocol.py +0 -0
  100. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_fetchmany_negative.py +0 -0
  101. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_handle_exception_hook.py +0 -0
  102. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_pickle_guards.py +0 -0
  103. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_slots.py +0 -0
  104. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_stale_rows.py +0 -0
  105. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_adapted_connection_public_paths.py +0 -0
  106. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_async_creator_fn.py +0 -0
  107. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_async_creator_fn_shape_check.py +0 -0
  108. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_await_staticmethod.py +0 -0
  109. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_final_close_suppression.py +0 -0
  110. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_loop_mismatch_remap.py +0 -0
  111. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_releases_inner_connection_pin.py +0 -0
  112. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_rollback_log.py +0 -0
  113. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_rollback_loop_already_running_remap.py +0 -0
  114. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_rollback_runtime_error_case_insensitive.py +0 -0
  115. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_suppress_and_soft_close_noop.py +0 -0
  116. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_cancellederror_force_close.py +0 -0
  117. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_in_greenlet_preflight.py +0 -0
  118. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_outside_greenlet.py +0 -0
  119. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_proxy_swap_arms.py +0 -0
  120. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_runtime_error_post_await.py +0 -0
  121. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_connect_construction_in_try_frame.py +0 -0
  122. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_connect_loop_state_remap.py +0 -0
  123. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_cursor_class_hooks.py +0 -0
  124. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_cursor_closed_before_server_side_precedence.py +0 -0
  125. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_cursor_time_error_remapped.py +0 -0
  126. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_dialect_do_close_releases_inner_strong_ref.py +0 -0
  127. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_close_fallback_force_close_transport.py +0 -0
  128. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_ping_cursor_construct_exception_arms.py +0 -0
  129. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_ping_override.py +0 -0
  130. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_ping_runtimeerror_returns_false.py +0 -0
  131. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_terminate_suppresses_exception.py +0 -0
  132. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_force_close_transport_dead_proxy.py +0 -0
  133. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_force_close_transport_docstring_pins.py +0 -0
  134. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_handle_exception_baseexceptiongroup_cancel_split.py +0 -0
  135. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_handle_exception_call_sites.py +0 -0
  136. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_handle_exception_delegates_to_remap_helper.py +0 -0
  137. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_is_disconnect_closed_proxy.py +0 -0
  138. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_no_in_function_imports.py +0 -0
  139. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_post_close_proxy_guards.py +0 -0
  140. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_terminate.py +0 -0
  141. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_terminate_close_idempotence.py +0 -0
  142. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_cursor_closed_check.py +0 -0
  143. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_execute_handle_exception.py +0 -0
  144. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_execute_suppress_cancellederror.py +0 -0
  145. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_init_sa_reference_signature.py +0 -0
  146. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_init_three_call_shapes.py +0 -0
  147. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_cursor_doc.py +0 -0
  148. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_cursor_sync_ctxmgr.py +0 -0
  149. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter.py +0 -0
  150. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_autocommit_property.py +0 -0
  151. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_closed_checks.py +0 -0
  152. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_lastrowid_sticky.py +0 -0
  153. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_op_lock_serialisation.py +0 -0
  154. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_terminate_handled_exceptions.py +0 -0
  155. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_commit_cancel_invalidates_slot.py +0 -0
  156. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_creator_fn_noncallable_rejected.py +0 -0
  157. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_after_connection_close.py +0 -0
  158. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_arraysize_accepts_zero_and_negative.py +0 -0
  159. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_arraysize_propagates_to_underlying.py +0 -0
  160. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_closed_guard.py +0 -0
  161. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_closed_guards.py +0 -0
  162. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_execute_close_suppress_behavioural.py +0 -0
  163. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_executemany_listifies_iterable.py +0 -0
  164. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_handle_exception.py +0 -0
  165. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_sync_only_context_manager.py +0 -0
  166. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_executemany_leader_flip_state_reset.py +0 -0
  167. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_handle_exception_case_insensitive.py +0 -0
  168. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_handle_exception_cause_walk.py +0 -0
  169. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_isolation_level.py +0 -0
  170. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_ping_cancel_cleanup.py +0 -0
  171. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_ping_close_arm_narrow_vs_programmer_bug.py +0 -0
  172. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_ping_single_execute_no_fetchone.py +0 -0
  173. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_ping_translates_dead_proxy_to_interface_error.py +0 -0
  174. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_audit_2026_05_round2_sa_changes.py +0 -0
  175. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_audit_2026_05_sa_changes.py +0 -0
  176. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_audit_2026_05_sa_coverage.py +0 -0
  177. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_close_runs_close_after_rollback_raise.py +0 -0
  178. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_collated_coverage_gaps.py +0 -0
  179. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_compiler_regexp_match.py +0 -0
  180. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_compliance_conftest_staleness_pin.py +0 -0
  181. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_connect_args_kwarg_validation.py +0 -0
  182. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_connect_args_value_validators.py +0 -0
  183. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_cross_package_version_parity.py +0 -0
  184. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_cursor_close_narrow_suppress.py +0 -0
  185. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_datetime_date_parse_errors.py +0 -0
  186. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_datetime_time_cross_type_payload.py +0 -0
  187. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_datetime_tz_conversion.py +0 -0
  188. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_description_alias_shared.py +0 -0
  189. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_detect_autocommit_setting.py +0 -0
  190. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dial_attempt_timeout_url_and_connect_args.py +0 -0
  191. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_dialect_config.py +0 -0
  192. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_docstring_pins.py +0 -0
  193. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_final_annotated.py +0 -0
  194. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_inherits_pysqlite.py +0 -0
  195. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_init_eager_autocommit_rejection.py +0 -0
  196. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_init_preserves_pins.py +0 -0
  197. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_ipv6_url.py +0 -0
  198. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_sqlite_incompatibility_flags_pinned.py +0 -0
  199. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_url_credentials.py +0 -0
  200. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_disconnect_substring_producer_emission.py +0 -0
  201. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_begin_cancel.py +0 -0
  202. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_begin_savepoint_asymmetry_documented.py +0 -0
  203. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_close_calls_dbapi_close_directly.py +0 -0
  204. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_close_fallback_suppress_arm.py +0 -0
  205. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_close_fallback_uses_sync_transport_teardown.py +0 -0
  206. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_close_first_arm_absorbs_runtime_error.py +0 -0
  207. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_executemany_local_override_pin.py +0 -0
  208. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_catches_programming_error.py +0 -0
  209. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_close_narrow.py +0 -0
  210. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_cursor_call_disconnect.py +0 -0
  211. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_database_error.py +0 -0
  212. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_dialect_specific_select_one.py +0 -0
  213. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_interface_error_with_code.py +0 -0
  214. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_non_disconnect_databaseerror_reraise.py +0 -0
  215. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_drift_defence_pins.py +0 -0
  216. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_drop_user_tables_log_record_bounded.py +0 -0
  217. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_floor_compile_time_gate.py +0 -0
  218. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_handle_exception_must_raise.py +0 -0
  219. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_handle_exception_remap.py +0 -0
  220. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_handle_exception_two_pass_recovery_after_mixed_cancel_group.py +0 -0
  221. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_bare_oserror_walked_not_early_return.py +0 -0
  222. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_case_insensitive.py +0 -0
  223. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_cause_walk.py +0 -0
  224. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_closed_handle_with_id_suffix.py +0 -0
  225. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_cluster_error.py +0 -0
  226. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_event_loop_already_running_prefix.py +0 -0
  227. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_event_loop_closed.py +0 -0
  228. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_event_loop_mismatch_substring.py +0 -0
  229. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_exception_group_and_substring_walk.py +0 -0
  230. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_interface_error_cause_walk.py +0 -0
  231. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_leader_lost_substring_case_insensitive.py +0 -0
  232. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_notfound_lookup_db.py +0 -0
  233. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_oe_code_restriction.py +0 -0
  234. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_protocol_error_arm.py +0 -0
  235. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_raft_collapse.py +0 -0
  236. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_raw_message.py +0 -0
  237. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_recognises_connection_invalidated_interfaceerror.py +0 -0
  238. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_recognises_cross_loop_interfaceerror.py +0 -0
  239. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_recognises_used_after_fork.py +0 -0
  240. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_substring_visible_within_dbapi_4kib_cap.py +0 -0
  241. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_untracked_savepoint_negative.py +0 -0
  242. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_walks_cause_oserror.py +0 -0
  243. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_isolation_level_in_connect_args_directional_message.py +0 -0
  244. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_isolation_lookup_vs_values_list_divergence_documented.py +0 -0
  245. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_leader_error_codes_identity.py +0 -0
  246. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_log_safe_peer_helper.py +0 -0
  247. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_log_safe_peer_routing.py +0 -0
  248. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_log_safe_peer_uses_sanitize_for_log.py +0 -0
  249. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_max_message_size_url_and_connect_args.py +0 -0
  250. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_max_total_rows_warning_does_not_advertise_disabled_token.py +0 -0
  251. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_phantom_returning_flag_does_not_exist_in_sa.py +0 -0
  252. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_create_drop_db_log_sanitises_ident.py +0 -0
  253. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_format_url_ident_sanitisation.py +0 -0
  254. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_reap_dbs_info_log_credential_mask_and_sanitize.py +0 -0
  255. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_reap_dbs_log_sanitized.py +0 -0
  256. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_session_token_shape.py +0 -0
  257. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_upsert.py +0 -0
  258. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_public_api.py +0 -0
  259. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_raft_collapse_markers_upstream_provenance.py +0 -0
  260. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_remap_loop_state_chains_from_matched_hop.py +0 -0
  261. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_remap_loop_state_helper_docstring_pin.py +0 -0
  262. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_requirements.py +0 -0
  263. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_requirements_closed_marker_present.py +0 -0
  264. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_requirements_recently_opened.py +0 -0
  265. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_reset_isolation_level_is_local_noop.py +0 -0
  266. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_result_processor_edge_branches.py +0 -0
  267. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_result_processor_log_payload_sanitized.py +0 -0
  268. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_result_processor_log_truncates_server_text.py +0 -0
  269. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_savepoint_leader_flip_loud_failure.py +0 -0
  270. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_stream_results_documented_eager_buffer.py +0 -0
  271. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_submodule_all.py +0 -0
  272. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_sync_connect_creator_fn_shape.py +0 -0
  273. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_sync_do_rollback_transport_fault_classified.py +0 -0
  274. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_sync_do_terminate.py +0 -0
  275. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_sync_do_terminate_log_includes_peer_and_id.py +0 -0
  276. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_time_colspec.py +0 -0
  277. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_time_tz_conversion.py +0 -0
  278. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_top_level_logger_has_null_handler.py +0 -0
  279. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_transitive_wire_import.py +0 -0
  280. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_url_int_upper_boundary_off_by_one.py +0 -0
  281. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_url_max_rows_none_token.py +0 -0
  282. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_url_pre_validation.py +0 -0
  283. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_url_timeout_validators_share_ssot.py +0 -0
  284. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_version.py +0 -0
  285. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_walk_cause_chain_caps_pathological_group_size.py +0 -0
  286. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_walk_cause_chain_cycle_at_depth.py +0 -0
  287. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_walk_cause_chain_depth_budget.py +0 -0
  288. {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_wire_decode_failed_prefix_constant_used_by_dialect.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlalchemy-dqlite
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: SQLAlchemy 2.0 dialect for dqlite distributed SQLite
5
5
  Project-URL: Homepage, https://github.com/letsdiscodev/sqlalchemy-dqlite
6
6
  Project-URL: Repository, https://github.com/letsdiscodev/sqlalchemy-dqlite
@@ -22,9 +22,9 @@ Classifier: Topic :: Database :: Database Engines/Servers
22
22
  Classifier: Topic :: Database :: Front-Ends
23
23
  Classifier: Typing :: Typed
24
24
  Requires-Python: >=3.13
25
- Requires-Dist: dqlite-client<0.3.0,>=0.2.0
26
- Requires-Dist: dqlite-dbapi<0.3.0,>=0.2.0
27
- Requires-Dist: dqlite-wire<0.3.0,>=0.2.0
25
+ Requires-Dist: dqlite-client<0.3.0,>=0.2.2
26
+ Requires-Dist: dqlite-dbapi<0.3.0,>=0.2.2
27
+ Requires-Dist: dqlite-wire<0.3.0,>=0.2.2
28
28
  Requires-Dist: sqlalchemy<3.0,>=2.0
29
29
  Provides-Extra: dev
30
30
  Requires-Dist: mypy>=1.0; extra == 'dev'
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sqlalchemy-dqlite"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "SQLAlchemy 2.0 dialect for dqlite distributed SQLite"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -26,15 +26,15 @@ classifiers = [
26
26
  "Typing :: Typed",
27
27
  ]
28
28
  dependencies = [
29
- "dqlite-dbapi>=0.2.0,<0.3.0",
29
+ "dqlite-dbapi>=0.2.2,<0.3.0",
30
30
  # The SA dialect imports dqliteclient and dqlitewire directly
31
31
  # (base.py / aio.py reach into client exception classes for
32
32
  # is_disconnect, and into dqlitewire for shared helpers). Pin
33
33
  # both transitively so a future major-version bump in either
34
34
  # package surfaces at install time, not at module-import time
35
35
  # in user code.
36
- "dqlite-client>=0.2.0,<0.3.0",
37
- "dqlite-wire>=0.2.0,<0.3.0",
36
+ "dqlite-client>=0.2.2,<0.3.0",
37
+ "dqlite-wire>=0.2.2,<0.3.0",
38
38
  "sqlalchemy>=2.0,<3.0",
39
39
  ]
40
40
 
@@ -27,7 +27,7 @@ from sqlalchemy.dialects.sqlite import (
27
27
  from sqlalchemydqlite.aio import DqliteDialect_aio
28
28
  from sqlalchemydqlite.base import DqliteCompiler, DqliteDialect
29
29
 
30
- __version__: _Final[str] = "0.2.0"
30
+ __version__: _Final[str] = "0.2.2"
31
31
 
32
32
  # SA convention (every shipped dialect package — sqlite, mysql, mssql,
33
33
  # oracle, postgresql): expose ``<package>.dialect`` pointing at the
@@ -514,6 +514,25 @@ class AsyncAdaptedCursor:
514
514
  very large bind set must drive multiple ``execute()`` calls
515
515
  themselves; ``executemany`` cannot stream because the retry
516
516
  contract requires re-iteration.
517
+
518
+ ``lastrowid`` handling: same sticky-INSERT contract as
519
+ ``execute`` (see ``execute``'s docstring for the full
520
+ rationale). Both branches below write ``self.lastrowid`` only
521
+ when the underlying cursor reports a non-None value (i.e., an
522
+ INSERT/REPLACE actually ran in this executemany); a subsequent
523
+ ``UPDATE``/``DELETE``/``SELECT`` executemany preserves the
524
+ prior INSERT's lastrowid, matching stdlib ``sqlite3.Cursor``
525
+ stickiness. DIVERGES from SA's aiosqlite reference
526
+ (``sqlalchemy/dialects/sqlite/aiosqlite.py``'s ``executemany``
527
+ body) which assigns ``self.lastrowid = _cursor.lastrowid``
528
+ unconditionally — under that shape, a non-INSERT
529
+ ``executemany`` clears the value to ``None``. Cross-driver
530
+ code consulting ``cursor.lastrowid`` after a non-INSERT
531
+ ``executemany`` sees the prior INSERT's value here vs ``None``
532
+ on aiosqlite. The dqlite stance privileges stdlib parity over
533
+ SA-reference parity; the choice is documented at both call
534
+ sites because the underlying RETURNING-path drain pattern
535
+ otherwise structurally mirrors the SA reference shape.
517
536
  """
518
537
  # Mirror the closed-cursor guard the other methods on this
519
538
  # class apply; see ``execute`` for the rationale.
@@ -933,11 +952,20 @@ class AsyncAdaptedConnection(AdaptedConnection):
933
952
  exposes it, and ``AttributeError`` on dqlite would force those
934
953
  tools onto a less-informative fallback path.
935
954
 
936
- Returns the union of the project-wide transport-class catch
937
- tuple (``OperationalError`` / ``InterfaceError`` /
938
- ``DqliteConnectionError`` / ``OSError``) plus
939
- ``asyncio.CancelledError`` — i.e., the same exceptions that the
940
- hand-rolled :meth:`terminate` body explicitly suppresses.
955
+ Returns the union of three groups, one per catch arm in the
956
+ hand-rolled :meth:`terminate` body:
957
+
958
+ * ``_TRANSPORT_CLASS_EXCEPTIONS`` — ``OperationalError`` /
959
+ ``InterfaceError`` / ``DqliteConnectionError`` / ``OSError``
960
+ (terminate() arm 1; the body DEBUG-logs and returns).
961
+ * ``RuntimeError`` — defunct-loop close shape
962
+ (``RuntimeError("Event loop is closed")``) from
963
+ ``engine.dispose()`` after an ``asyncio.run()`` per-call
964
+ tore the loop down (terminate() arm 2; the body
965
+ DEBUG-logs and runs ``_force_close_transport``).
966
+ * ``asyncio.CancelledError`` — outer cancel during forced
967
+ reclaim (terminate() arm 3; the body runs
968
+ ``_force_close_transport`` and re-raises).
941
969
 
942
970
  ``terminate()`` itself stays hand-rolled rather than reusing
943
971
  SA's ``AsyncAdapt_terminate`` mixin: the dqlite lifecycle
@@ -945,9 +973,11 @@ class AsyncAdaptedConnection(AdaptedConnection):
945
973
  both ``_terminate_graceful_close`` and ``_terminate_force_close``
946
974
  against an inert template. This method exists purely so
947
975
  introspection-only callers see a tuple
948
- that matches the hand-rolled body's catch arms.
976
+ that matches the hand-rolled body's catch arms. Any new
977
+ ``except`` arm added to ``terminate()`` must be reflected
978
+ here to keep the contract honoured.
949
979
  """
950
- return _TRANSPORT_CLASS_EXCEPTIONS + (asyncio.CancelledError,)
980
+ return _TRANSPORT_CLASS_EXCEPTIONS + (RuntimeError, asyncio.CancelledError)
951
981
 
952
982
  def __init__(
953
983
  self,
@@ -991,6 +1021,42 @@ class AsyncAdaptedConnection(AdaptedConnection):
991
1021
  else:
992
1022
  inner_conn = connection
993
1023
  dbapi_module = dbapi
1024
+ # Type-shape guard: detect the third plausible call shape —
1025
+ # the positional swap ``AsyncAdaptedConnection(connection,
1026
+ # dbapi)`` — that the ``_UNSET`` sentinel cannot discriminate.
1027
+ # Without this guard the swap silently poisons both slots and
1028
+ # the downstream ``AttributeError`` (on ``self._connection.cursor()``
1029
+ # or third-party ``self.dbapi.OperationalError`` lookup) surfaces
1030
+ # many frames from the construction site.
1031
+ #
1032
+ # Confirmed-swap signature: the supposed dbapi (first
1033
+ # positional) lacks ``OperationalError`` but exposes
1034
+ # ``cursor`` — i.e. it shape-looks like a connection — AND
1035
+ # the supposed connection (second positional) lacks
1036
+ # ``cursor`` but exposes ``OperationalError`` — i.e. it
1037
+ # shape-looks like a dbapi module. Both conditions firing
1038
+ # together is the unambiguous swap fingerprint; a single
1039
+ # mismatch could be a minimal in-tree fake that lacks both
1040
+ # attributes by design (the test suite has several), so the
1041
+ # guard tolerates that shape and only rejects the
1042
+ # bilaterally swapped call.
1043
+ if (
1044
+ dbapi_module is not None
1045
+ and inner_conn is not None
1046
+ and not hasattr(dbapi_module, "OperationalError")
1047
+ and hasattr(dbapi_module, "cursor")
1048
+ and not hasattr(inner_conn, "cursor")
1049
+ and hasattr(inner_conn, "OperationalError")
1050
+ ):
1051
+ raise TypeError(
1052
+ f"AsyncAdaptedConnection __init__: positional "
1053
+ f"arguments appear swapped — first positional "
1054
+ f"({type(dbapi_module).__name__}) has 'cursor' but "
1055
+ f"no 'OperationalError'; second positional "
1056
+ f"({type(inner_conn).__name__}) has "
1057
+ f"'OperationalError' but no 'cursor'. Reference "
1058
+ f"shape: AsyncAdaptedConnection(dbapi, connection)."
1059
+ )
994
1060
  # ``_connection`` is the concrete ``dqlitedbapi.aio.AsyncConnection``
995
1061
  # this adapter wraps; SQLAlchemy's parent ``AdaptedConnection``
996
1062
  # declares the attribute with a wider Protocol type, so we keep
@@ -1321,6 +1387,24 @@ class AsyncAdaptedConnection(AdaptedConnection):
1321
1387
  # raises from the matched hop; if no hop matches, falls
1322
1388
  # through to the wrap below.
1323
1389
  _remap_loop_state_runtime_error(remainder)
1390
+ # Single-child remainder unwrap: a ``TaskGroup`` /
1391
+ # ``anyio.create_task_group`` wrapping ONE inner SQL
1392
+ # operation produces a group-of-one whose only child is
1393
+ # the original ``IntegrityError`` / ``DataError`` /
1394
+ # ``ProgrammingError`` / etc. Unconditionally wrapping
1395
+ # the remainder as ``OperationalError(code=None)`` would
1396
+ # silently lose the original class identity — user
1397
+ # ``except IntegrityError`` clauses miss the rewritten
1398
+ # exception and ``is_disconnect``'s substring scan runs
1399
+ # on an aggregate message that isn't a wire fault. The
1400
+ # group-of-one case is structurally byte-equivalent to
1401
+ # the non-group path; dispatch through the same shape.
1402
+ # ``raise child from remainder`` preserves the group on
1403
+ # ``__cause__`` so SA's ``_walk_cause_chain`` still finds
1404
+ # the structured-concurrency context if it needs to.
1405
+ if len(remainder.exceptions) == 1:
1406
+ child = remainder.exceptions[0]
1407
+ raise child from remainder
1324
1408
  child_classes = {type(c).__name__ for c in remainder.exceptions}
1325
1409
  raise OperationalError(
1326
1410
  f"aggregate {type(remainder).__name__} with "
@@ -1460,8 +1544,17 @@ class AsyncAdaptedConnection(AdaptedConnection):
1460
1544
  # leak bare ``RuntimeError`` past
1461
1545
  # ``engine.dispose()``.
1462
1546
  msg_lower = str(exc).lower()
1547
+ # Use ``if / elif / else`` (not sequential
1548
+ # ``if``s) so the mutual-exclusion contract is
1549
+ # structural rather than dependent on
1550
+ # ``_handle_exception``'s ``NoReturn`` annotation
1551
+ # (which has no runtime enforcement). A future
1552
+ # refactor that breaks the NoReturn invariant —
1553
+ # or a contributor adding a fourth substring arm
1554
+ # — cannot accidentally fall through to the
1555
+ # trailing ``raise``.
1463
1556
  if "different loop" in msg_lower or "different event loop" in msg_lower:
1464
- self._handle_exception(exc)
1557
+ self._handle_exception(exc) # NoReturn
1465
1558
  # ``RuntimeError("Event loop is closed")`` lands
1466
1559
  # here during ``engine.dispose()`` after a per-call
1467
1560
  # ``asyncio.run()`` finished and tore the loop down
@@ -1472,7 +1565,7 @@ class AsyncAdaptedConnection(AdaptedConnection):
1472
1565
  # try/finally still runs the proxy swap on this
1473
1566
  # return path. The debug log preserves the
1474
1567
  # traceback for triage.
1475
- if "event loop is closed" in msg_lower:
1568
+ elif "event loop is closed" in msg_lower:
1476
1569
  peer = _log_safe_peer(self._connection)
1477
1570
  logger.debug(
1478
1571
  "AsyncAdaptedConnection.close (id=%s, peer=%s): "
@@ -1494,10 +1587,11 @@ class AsyncAdaptedConnection(AdaptedConnection):
1494
1587
  # the close-arm matches that discipline rather than
1495
1588
  # leaking a bare ``RuntimeError`` past
1496
1589
  # ``engine.dispose()``.
1497
- if "loop is already running" in msg_lower:
1498
- self._handle_exception(exc)
1499
- # Other RuntimeErrors (programmer bugs) propagate.
1500
- raise
1590
+ elif "loop is already running" in msg_lower:
1591
+ self._handle_exception(exc) # NoReturn
1592
+ else:
1593
+ # Other RuntimeErrors (programmer bugs) propagate.
1594
+ raise
1501
1595
  # The non-greenlet path is handled by the
1502
1596
  # ``in_greenlet()`` preflight at the top of ``close()``;
1503
1597
  # ``MissingGreenlet`` cannot land here.
@@ -1613,18 +1707,25 @@ class AsyncAdaptedConnection(AdaptedConnection):
1613
1707
  on a single supported method instead of walking three layers
1614
1708
  of private attributes.
1615
1709
 
1616
- Idempotent. Absorbs every ``Exception`` and
1617
- ``asyncio.CancelledError`` from the hook call a missing
1618
- hook (older dbapi version), a dead ``weakref.proxy``
1619
- ``ReferenceError`` (the post-``_release_inner_strong_ref``
1620
- state), or a writer.close() failure is silently swallowed
1621
- and logged. Does NOT catch ``BaseException`` shapes that
1622
- signal cooperative interpreter shutdown
1623
- ``KeyboardInterrupt`` and ``SystemExit`` (and any other
1624
- non-``Exception`` ``BaseException``) propagate so a
1625
- signal-handler-driven ``engine.dispose()`` cannot mask
1626
- them. Mirrors the discipline ``DqliteDialect.do_terminate``
1627
- documents at ``base.py``.
1710
+ Idempotent. Absorbs every ``Exception`` from the hook call
1711
+ a missing hook (older dbapi version), a dead
1712
+ ``weakref.proxy`` ``ReferenceError`` (the post-
1713
+ ``_release_inner_strong_ref`` state), or a writer.close()
1714
+ failure is silently swallowed and logged. Does NOT catch
1715
+ ``asyncio.CancelledError`` (a ``BaseException`` subclass
1716
+ since 3.8): asyncio's structured-concurrency contract
1717
+ requires the cancel signal to reach the parent
1718
+ ``TaskGroup`` / ``asyncio.timeout`` so the parent's wait
1719
+ completes with cancelled status. Callers that need to run
1720
+ the force-close on a cancel path catch ``CancelledError``
1721
+ themselves and re-raise after invoking this helper (see the
1722
+ close-arm at the ``except asyncio.CancelledError`` site and
1723
+ the terminate-arm's symmetric handler). Also does NOT catch
1724
+ ``KeyboardInterrupt`` / ``SystemExit`` (and any other
1725
+ non-``Exception`` ``BaseException``) so a signal-handler-
1726
+ driven ``engine.dispose()`` cannot mask them. Mirrors the
1727
+ discipline ``DqliteDialect.do_terminate`` documents at
1728
+ ``base.py``.
1628
1729
 
1629
1730
  The two ``getattr`` reads on ``self._connection`` sit
1630
1731
  INSIDE the try frame so a ``ReferenceError`` from a dead
@@ -1659,13 +1760,21 @@ class AsyncAdaptedConnection(AdaptedConnection):
1659
1760
  id(self),
1660
1761
  peer,
1661
1762
  )
1662
- except (Exception, asyncio.CancelledError) as exc: # pragma: no cover - defensive
1663
- # Narrow to ``(Exception, asyncio.CancelledError)`` so a
1664
- # greenlet-level cancel from SA's pool-dispose path is
1665
- # still absorbed (``CancelledError`` is a ``BaseException``
1666
- # subclass since 3.8) but ``KeyboardInterrupt`` /
1667
- # ``SystemExit`` propagate. Mirrors the sibling cursor-
1668
- # close discipline upstream in this module.
1763
+ except Exception as exc: # pragma: no cover - defensive
1764
+ # Narrow to ``Exception`` only — ``asyncio.CancelledError``
1765
+ # (a ``BaseException`` subclass since 3.8) must propagate
1766
+ # so an outer ``asyncio.TaskGroup`` / ``asyncio.timeout``
1767
+ # / ``anyio.create_task_group`` parent that issued the
1768
+ # cancel sees the child complete with cancelled status.
1769
+ # Absorbing the cancel here would leave the parent
1770
+ # waiting indefinitely on its ``__aexit__`` for a
1771
+ # cancel-acknowledgement that never arrives. The
1772
+ # close-arm and terminate-arm call sites catch
1773
+ # ``CancelledError`` externally, invoke this helper, and
1774
+ # then re-raise — that pattern preserves the cancel
1775
+ # without requiring this helper to swallow it.
1776
+ # ``KeyboardInterrupt`` / ``SystemExit`` propagate by the
1777
+ # same ``BaseException`` rule.
1669
1778
  logger.debug(
1670
1779
  "AsyncAdaptedConnection._force_close_transport (id=%s, peer=%s): "
1671
1780
  "best-effort sync close raised (%s); ignoring",
@@ -1861,20 +1970,23 @@ class DqliteDialect_aio(DqliteDialect):
1861
1970
  is deliberately NOT caught — asyncio's structured-concurrency
1862
1971
  contract says cancels must propagate, and an outer cancel
1863
1972
  signalling "abort dispose now" must not be silently swallowed.
1973
+
1974
+ Two-tier catch (mirrors the sync sibling):
1975
+
1976
+ * ``_FORCE_CLOSE_TAIL_EXCEPTIONS`` — expected transport-class
1977
+ shapes. DEBUG-log + absorb.
1978
+ * Any other ``Exception`` — most likely a cross-repo dbapi
1979
+ refactor regression. WARNING-log + absorb so the SA contract
1980
+ holds while the regression stays loudly observable.
1864
1981
  """
1865
1982
  peer = _log_safe_peer(dbapi_connection)
1866
1983
  try:
1867
1984
  dbapi_connection.terminate()
1868
1985
  except _FORCE_CLOSE_TAIL_EXCEPTIONS:
1869
- # Narrow tuple: covers every transport-class shape so the
1870
- # has_terminate=True non-raising contract holds for real
1871
- # transport failures (OSError, dbapi.Error subclasses,
1872
- # DqliteConnectionError). AttributeError / TypeError from
1873
- # cross-repo dbapi-package refactor breaks (where
1874
- # ``terminate()`` is removed or renamed) propagate so the
1875
- # regression surfaces loudly rather than silent no-op on
1876
- # every ``engine.dispose()``. Sync sibling at
1877
- # base.py::do_terminate uses the same tuple.
1986
+ # Expected shapes DEBUG-log + absorb. Narrow tuple covers
1987
+ # transport-class failures (OSError + dbapi.Error subclasses
1988
+ # + DqliteConnectionError + RuntimeError + ReferenceError)
1989
+ # that ``terminate()`` legitimately surfaces.
1878
1990
  logger.debug(
1879
1991
  "do_terminate: terminate raised on dispose for peer=%s id=%s; "
1880
1992
  "proceeding (has_terminate=True non-raising contract)",
@@ -1882,6 +1994,25 @@ class DqliteDialect_aio(DqliteDialect):
1882
1994
  id(dbapi_connection),
1883
1995
  exc_info=True,
1884
1996
  )
1997
+ except Exception:
1998
+ # Unexpected shape — most likely a cross-repo dbapi
1999
+ # refactor regression (``terminate`` renamed, removed,
2000
+ # signature-changed, or swapped to a raising property).
2001
+ # SA's ``has_terminate=True`` contract is binary non-
2002
+ # raising; absorbing here prevents the regression from
2003
+ # aborting ``engine.dispose()`` and leaking sibling slots.
2004
+ # WARNING-tier (vs DEBUG) so the regression stays loudly
2005
+ # visible in operator logs. Sync sibling at
2006
+ # base.py::do_terminate uses the same two-tier shape.
2007
+ logger.warning(
2008
+ "do_terminate: terminate raised UNEXPECTED exception type "
2009
+ "on dispose for peer=%s id=%s; SA has_terminate=True "
2010
+ "contract absorbs to prevent dispose abort. Likely a "
2011
+ "dbapi-side refactor regression — investigate.",
2012
+ peer,
2013
+ id(dbapi_connection),
2014
+ exc_info=True,
2015
+ )
1885
2016
 
1886
2017
  def do_ping(self, dbapi_connection: Any) -> bool:
1887
2018
  """Async-side bespoke ping.
@@ -1978,6 +2109,22 @@ class DqliteDialect_aio(DqliteDialect):
1978
2109
  # and SA's ``_handle_dbapi_exception`` see a clean
1979
2110
  # ``dbapi.Error``.
1980
2111
  if type(dbapi_connection._connection) in weakref.ProxyTypes:
2112
+ # Diagnostic: SA's pool sees this raise as a ping failure
2113
+ # and retires the slot silently. Without a DEBUG line a
2114
+ # flapping pool's root cause (sibling task closed the
2115
+ # adapter between pool checkout and ping, GC sweep,
2116
+ # engine.dispose race) is invisible to log analysis.
2117
+ # Sibling close()/terminate()/transport-class arms in this
2118
+ # module emit DEBUG signal on analogous slot-retirement
2119
+ # transitions; mirror that discipline here.
2120
+ logger.debug(
2121
+ "_async_ping: adapter already closed (id=%s); reporting "
2122
+ "ping failure so SA pool retires the slot. Likely a "
2123
+ "sibling task closed the adapter between pool checkout "
2124
+ "and ping (GC sweep, engine.dispose race, parallel "
2125
+ "task close).",
2126
+ id(dbapi_connection),
2127
+ )
1981
2128
  raise InterfaceError(f"Connection is closed (id={id(dbapi_connection)})")
1982
2129
  try:
1983
2130
  cur = dbapi_connection._connection.cursor()
@@ -2181,11 +2328,17 @@ class DqliteDialect_aio(DqliteDialect):
2181
2328
  # ``RuntimeWarning: coroutine '...' was never awaited``.
2182
2329
  # Surface the misuse with ``ArgumentError`` at connect time so
2183
2330
  # the diagnostic points at the configuration site.
2184
- # ``inspect.iscoroutinefunction`` on Python 3.12+ recognises
2331
+ # ``inspect.iscoroutinefunction`` recognises
2185
2332
  # ``functools.partial`` wrappers around an ``async def`` (a
2186
- # legitimate user pattern). The check mirrors SA's overall
2187
- # trust-the-creator discipline while catching the single most
2188
- # common user mistake.
2333
+ # legitimate user pattern). That unwrap behaviour was added in
2334
+ # CPython 3.12 (cpython#101174). The project's
2335
+ # ``pyproject.toml`` pins ``requires-python = ">=3.13"`` so
2336
+ # the partial-detection path is structurally guaranteed; any
2337
+ # future relaxation of the requires-python floor below 3.12
2338
+ # would silently leave the partial-wrapper case undetected
2339
+ # and must re-evaluate this gate. The check mirrors SA's
2340
+ # overall trust-the-creator discipline while catching the
2341
+ # single most common user mistake.
2189
2342
  if creator_fn is not None and not callable(creator_fn):
2190
2343
  raise ArgumentError(
2191
2344
  f"async_creator_fn must be callable; got "