sqlalchemy-dqlite 0.2.2__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/DEVELOPMENT.md +35 -1
- sqlalchemy_dqlite-0.3.0/PKG-INFO +113 -0
- sqlalchemy_dqlite-0.3.0/README.md +77 -0
- sqlalchemy_dqlite-0.3.0/docs/connection-url.md +31 -0
- sqlalchemy_dqlite-0.3.0/docs/limitations.md +31 -0
- sqlalchemy_dqlite-0.3.0/docs/transactions.md +50 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/pyproject.toml +4 -4
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/src/sqlalchemydqlite/__init__.py +5 -15
- sqlalchemy_dqlite-0.3.0/src/sqlalchemydqlite/aio.py +1000 -0
- sqlalchemy_dqlite-0.3.0/src/sqlalchemydqlite/base.py +1769 -0
- sqlalchemy_dqlite-0.3.0/src/sqlalchemydqlite/provision.py +298 -0
- sqlalchemy_dqlite-0.3.0/src/sqlalchemydqlite/requirements.py +532 -0
- sqlalchemy_dqlite-0.3.0/tests/compliance/conftest.py +84 -0
- sqlalchemy_dqlite-0.3.0/tests/compliance/test_suite.py +7 -0
- sqlalchemy_dqlite-0.3.0/tests/conftest.py +71 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/conftest.py +0 -2
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_async_cancel_during_commit_invalidates_slot.py +23 -65
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_async_cancel_inside_engine_begin.py +5 -22
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_async_concurrent_sessions.py +12 -56
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_async_context.py +2 -5
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_async_executemany.py +7 -26
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_async_savepoint_cancellation_invalidates_slot.py +11 -45
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_bulk_dml_returning.py +1 -9
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_concurrent_writers_begin_immediate.py +4 -24
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_date_tz_drop.py +2 -15
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_dial_func_via_connect_args_reaches_dbapi.py +3 -15
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_dqlite_session_mode_execution_option.py +21 -72
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_engine_begin_is_atomic.py +15 -55
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_engine_events_fire.py +5 -17
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_inspect_async_and_close_parity.py +10 -46
- sqlalchemy_dqlite-0.3.0/tests/integration/test_isolation_level_pin.py +38 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_json_column.py +9 -27
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_native_boolean.py +25 -10
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_orm_operations.py +3 -13
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_pool_pre_ping_leader_flip.py +8 -40
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_pool_recycle_and_dispose_fan_out.py +14 -55
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_pool_reset_on_return.py +5 -22
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_reflect_column_types.py +3 -11
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_savepoint_basic.py +2 -14
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_savepoint_orm_session_begin_nested.py +2 -26
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_savepoint_quoted_identifiers.py +9 -23
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_temp_table_scoping_per_connection.py +7 -26
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_time_and_reflection.py +4 -13
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/test_upsert.py +6 -19
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_2pc_not_supported_error.py +5 -22
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_adapter_arraysize_default_and_semantics.py +6 -18
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_adapter_arraysize_validation.py +1 -9
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_adapter_cursor_iterator_protocol.py +2 -12
- sqlalchemy_dqlite-0.3.0/tests/test_adapter_fetchmany_negative.py +35 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_adapter_handle_exception_hook.py +1 -12
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_adapter_pickle_guards.py +2 -7
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_adapter_slots.py +2 -5
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_adapter_stale_rows.py +4 -9
- sqlalchemy_dqlite-0.3.0/tests/test_aio_adapted_connection_public_paths.py +107 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_async_creator_fn.py +9 -50
- sqlalchemy_dqlite-0.3.0/tests/test_aio_async_creator_fn_shape_check.py +53 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_async_ping_closed_proxy_log.py +3 -15
- sqlalchemy_dqlite-0.3.0/tests/test_aio_await_staticmethod.py +21 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_close_final_close_suppression.py +7 -25
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_close_loop_mismatch_remap.py +2 -12
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_close_releases_inner_connection_pin.py +6 -30
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_close_rollback_log.py +9 -42
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_close_rollback_loop_already_running_remap.py +4 -21
- sqlalchemy_dqlite-0.3.0/tests/test_aio_close_rollback_runtime_error_case_insensitive.py +41 -0
- sqlalchemy_dqlite-0.3.0/tests/test_aio_close_suppress_and_soft_close_noop.py +80 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_close_terminate_cancellederror_force_close.py +8 -29
- sqlalchemy_dqlite-0.3.0/tests/test_aio_close_terminate_in_greenlet_preflight.py +42 -0
- sqlalchemy_dqlite-0.3.0/tests/test_aio_close_terminate_outside_greenlet.py +124 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_close_terminate_proxy_swap_arms.py +25 -87
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_close_terminate_runtime_error_post_await.py +11 -43
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_connect_construction_in_try_frame.py +6 -33
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_connect_loop_state_remap.py +8 -34
- sqlalchemy_dqlite-0.3.0/tests/test_aio_cursor_class_hooks.py +78 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_cursor_closed_before_server_side_precedence.py +4 -16
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_cursor_time_error_remapped.py +5 -22
- sqlalchemy_dqlite-0.3.0/tests/test_aio_dialect_do_close_releases_inner_strong_ref.py +66 -0
- sqlalchemy_dqlite-0.3.0/tests/test_aio_do_close_fallback_force_close_transport.py +69 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_do_ping_cursor_construct_exception_arms.py +4 -17
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_do_ping_override.py +7 -35
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_do_ping_runtimeerror_returns_false.py +3 -9
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_do_terminate_suppresses_exception.py +4 -15
- sqlalchemy_dqlite-0.3.0/tests/test_aio_force_close_transport_cancelled_error.py +46 -0
- sqlalchemy_dqlite-0.3.0/tests/test_aio_force_close_transport_dead_proxy.py +63 -0
- sqlalchemy_dqlite-0.3.0/tests/test_aio_force_close_transport_docstring_pins.py +40 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_handle_exception_baseexceptiongroup_cancel_split.py +22 -88
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_handle_exception_call_sites.py +5 -17
- sqlalchemy_dqlite-0.3.0/tests/test_aio_handle_exception_delegates_to_remap_helper.py +64 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_handle_exception_group_single_child_unwrap.py +11 -25
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_is_disconnect_closed_proxy.py +9 -21
- sqlalchemy_dqlite-0.3.0/tests/test_aio_post_close_proxy_guards.py +97 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_terminate.py +22 -50
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_aio_terminate_close_idempotence.py +5 -18
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapted_connection_cursor_closed_check.py +9 -23
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapted_connection_execute_handle_exception.py +5 -24
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapted_connection_execute_suppress_cancellederror.py +3 -11
- sqlalchemy_dqlite-0.3.0/tests/test_async_adapted_connection_init_positional_swap_guard.py +60 -0
- sqlalchemy_dqlite-0.3.0/tests/test_async_adapted_connection_init_sa_reference_signature.py +31 -0
- sqlalchemy_dqlite-0.3.0/tests/test_async_adapted_connection_init_three_call_shapes.py +48 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapted_cursor_sync_ctxmgr.py +7 -24
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapter.py +48 -152
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapter_autocommit_property.py +5 -8
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapter_closed_checks.py +6 -11
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapter_lastrowid_sticky.py +7 -20
- sqlalchemy_dqlite-0.3.0/tests/test_async_adapter_op_lock_serialisation.py +70 -0
- sqlalchemy_dqlite-0.3.0/tests/test_async_adapter_terminate_handled_exceptions.py +21 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_adapter_terminate_handled_exceptions_runtimeerror.py +3 -15
- sqlalchemy_dqlite-0.3.0/tests/test_async_commit_cancel_invalidates_slot.py +86 -0
- sqlalchemy_dqlite-0.3.0/tests/test_async_creator_fn_noncallable_rejected.py +20 -0
- sqlalchemy_dqlite-0.3.0/tests/test_async_cursor_after_connection_close.py +37 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_cursor_arraysize_accepts_zero_and_negative.py +3 -22
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_cursor_arraysize_propagates_to_underlying.py +5 -25
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_cursor_closed_guard.py +1 -11
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_cursor_closed_guards.py +6 -19
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_cursor_execute_close_suppress_behavioural.py +8 -39
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_cursor_executemany_listifies_iterable.py +5 -18
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_cursor_handle_exception.py +4 -13
- sqlalchemy_dqlite-0.3.0/tests/test_async_cursor_sync_only_context_manager.py +27 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_dialect_isolation_set.py +5 -21
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_executemany_leader_flip_state_reset.py +8 -31
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_handle_exception_case_insensitive.py +4 -17
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_handle_exception_cause_walk.py +8 -29
- sqlalchemy_dqlite-0.3.0/tests/test_async_isolation_level.py +30 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_ping_cancel_cleanup.py +21 -78
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_ping_close_arm_narrow_vs_programmer_bug.py +5 -14
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_ping_translates_dead_proxy_to_interface_error.py +5 -24
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_audit_2026_05_round2_sa_changes.py +12 -44
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_audit_2026_05_sa_changes.py +17 -46
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_audit_2026_05_sa_coverage.py +16 -79
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_bind_side_cross_type_rejection.py +11 -35
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_busy_timeout_url_and_connect_args.py +11 -37
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_check_same_thread_url_and_connect_args.py +5 -38
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_close_runs_close_after_rollback_raise.py +8 -28
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_collated_coverage_gaps.py +6 -18
- sqlalchemy_dqlite-0.3.0/tests/test_colspecs_pysqlite_time_inherited_baseline.py +17 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_compiler_regexp_match.py +8 -17
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_compliance_conftest_staleness_pin.py +10 -37
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_connect_args_kwarg_validation.py +8 -20
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_connect_args_value_validators.py +25 -60
- sqlalchemy_dqlite-0.3.0/tests/test_cross_package_version_parity.py +93 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_cursor_close_narrow_suppress.py +4 -16
- sqlalchemy_dqlite-0.3.0/tests/test_datetime_bind_widens_date.py +40 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_datetime_date_parse_errors.py +2 -10
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_datetime_time_cross_type_payload.py +7 -40
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_datetime_tz_conversion.py +3 -13
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_ddl_kwarg_dqlite_prefix_rejected.py +4 -22
- sqlalchemy_dqlite-0.3.0/tests/test_ddl_kwarg_prefix_documented.py +36 -0
- sqlalchemy_dqlite-0.3.0/tests/test_description_alias_shared.py +17 -0
- sqlalchemy_dqlite-0.3.0/tests/test_detect_autocommit_setting.py +68 -0
- sqlalchemy_dqlite-0.3.0/tests/test_dial_attempt_timeout_url_and_connect_args.py +51 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dialect.py +59 -303
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dialect_dialect_config.py +35 -127
- sqlalchemy_dqlite-0.3.0/tests/test_dialect_docstring_pins.py +11 -0
- sqlalchemy_dqlite-0.3.0/tests/test_dialect_final_annotated.py +10 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dialect_inherits_pysqlite.py +2 -15
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dialect_init_eager_autocommit_rejection.py +4 -19
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dialect_init_preserves_pins.py +13 -46
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dialect_ipv6_url.py +3 -23
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dialect_sqlite_incompatibility_flags_pinned.py +2 -17
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dialect_url_credentials.py +0 -1
- sqlalchemy_dqlite-0.3.0/tests/test_disconnect_substring_producer_emission.py +125 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_begin_cancel.py +4 -12
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_begin_cursor_close_widened_catch.py +10 -29
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_begin_emits_begin.py +14 -65
- sqlalchemy_dqlite-0.3.0/tests/test_do_close_calls_dbapi_close_directly.py +91 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_close_fallback_suppress_arm.py +11 -31
- sqlalchemy_dqlite-0.3.0/tests/test_do_close_fallback_uses_sync_transport_teardown.py +33 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_close_first_arm_absorbs_runtime_error.py +9 -23
- sqlalchemy_dqlite-0.3.0/tests/test_do_execute_local_override_pin.py +49 -0
- sqlalchemy_dqlite-0.3.0/tests/test_do_executemany_local_override_pin.py +35 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_ping_catches_programming_error.py +4 -16
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_ping_close_narrow.py +10 -30
- sqlalchemy_dqlite-0.3.0/tests/test_do_ping_cursor_call_disconnect.py +70 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_ping_database_error.py +5 -12
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_ping_dialect_specific_select_one.py +1 -12
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_ping_interface_error_with_code.py +4 -15
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_ping_non_disconnect_databaseerror_reraise.py +2 -12
- sqlalchemy_dqlite-0.3.0/tests/test_do_recover_twophase_noreturn_annotation.py +16 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_terminate_narrow_catch_propagates_refactor_break.py +2 -19
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_do_terminate_non_raising_contract_widened.py +3 -30
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dqlite_datetime_bind_zero_microseconds.py +2 -23
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dqlite_datetime_time_literal_processor_microseconds.py +3 -22
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dqlite_datetime_types_reject_pysqlite_kwargs.py +3 -19
- sqlalchemy_dqlite-0.3.0/tests/test_dqlite_time_bind_processor_combined_fixes.py +48 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dqlite_time_result_processor_preserves_offset.py +3 -25
- sqlalchemy_dqlite-0.3.0/tests/test_dqlitetime_bind_processor_returns_none.py +21 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_dqlitetime_cross_axis_narrow_with_timezone_true.py +3 -22
- sqlalchemy_dqlite-0.3.0/tests/test_drift_defence_pins.py +36 -0
- sqlalchemy_dqlite-0.3.0/tests/test_drop_user_tables_log_record_bounded.py +33 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_floor_compile_time_gate.py +9 -37
- sqlalchemy_dqlite-0.3.0/tests/test_handle_exception_must_raise.py +35 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_handle_exception_remap.py +7 -22
- sqlalchemy_dqlite-0.3.0/tests/test_handle_exception_two_pass_recovery_after_mixed_cancel_group.py +52 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_bare_databaseerror_type_identity.py +90 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_bare_dbe_short_circuits_on_slot_fatal_codes.py +73 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_bare_oserror_walked_not_early_return.py +4 -25
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_case_insensitive.py +2 -4
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_cause_walk.py +11 -35
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_closed_handle_with_id_suffix.py +24 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_cluster_error.py +8 -28
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_database_error_substring.py +73 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_event_loop_already_running_prefix.py +34 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_event_loop_closed.py +3 -15
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_event_loop_mismatch_substring.py +52 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_exception_group_and_substring_walk.py +7 -20
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_interface_error_cause_walk.py +8 -33
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_leader_lost_substring_case_insensitive.py +5 -19
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_notfound_lookup_db.py +8 -43
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_oe_code_restriction.py +28 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_protocol_error_arm.py +41 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_raft_collapse.py +7 -30
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_raw_message.py +42 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_recognises_connection_invalidated_interfaceerror.py +59 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_recognises_cross_loop_interfaceerror.py +76 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_recognises_used_after_fork.py +41 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_substring_visible_within_dbapi_4kib_cap.py +40 -0
- sqlalchemy_dqlite-0.3.0/tests/test_is_disconnect_untracked_savepoint_negative.py +57 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_is_disconnect_walks_cause_oserror.py +7 -22
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_isolation_level_in_connect_args_directional_message.py +5 -13
- sqlalchemy_dqlite-0.3.0/tests/test_isolation_lookup_vs_values_list_divergence_documented.py +58 -0
- sqlalchemy_dqlite-0.3.0/tests/test_leader_error_codes_identity.py +12 -0
- sqlalchemy_dqlite-0.3.0/tests/test_log_safe_peer_helper.py +48 -0
- sqlalchemy_dqlite-0.3.0/tests/test_log_safe_peer_uses_sanitize_for_log.py +33 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_max_message_size_url_and_connect_args.py +6 -39
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_max_total_rows_warning_does_not_advertise_disabled_token.py +4 -27
- sqlalchemy_dqlite-0.3.0/tests/test_phantom_returning_flag_does_not_exist_in_sa.py +25 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_create_drop_db_log_sanitises_ident.py +2 -11
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_drop_user_tables_and_reap.py +17 -58
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_drop_user_tables_log_sanitized.py +5 -18
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_drop_user_tables_quote_escaping.py +6 -28
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_format_url_ident_sanitisation.py +10 -42
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_generate_driver_url.py +6 -27
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_reap_dbs_info_log_credential_mask_and_sanitize.py +5 -20
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_reap_dbs_log_sanitized.py +3 -14
- sqlalchemy_dqlite-0.3.0/tests/test_provision_session_token_shape.py +31 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_provision_upsert.py +3 -13
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_public_api.py +12 -43
- sqlalchemy_dqlite-0.3.0/tests/test_raft_collapse_markers_upstream_provenance.py +35 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_remap_loop_state_chains_from_matched_hop.py +9 -34
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_requirements.py +7 -19
- sqlalchemy_dqlite-0.3.0/tests/test_requirements_closed_marker_present.py +42 -0
- sqlalchemy_dqlite-0.3.0/tests/test_requirements_recently_opened.py +33 -0
- sqlalchemy_dqlite-0.3.0/tests/test_reset_isolation_level_is_local_noop.py +37 -0
- sqlalchemy_dqlite-0.3.0/tests/test_result_processor_debug_arm_isenabledfor_guard.py +140 -0
- sqlalchemy_dqlite-0.3.0/tests/test_result_processor_edge_branches.py +36 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_result_processor_log_payload_sanitized.py +5 -16
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_result_processor_log_truncates_server_text.py +5 -25
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_result_processor_warning_one_shot.py +5 -23
- sqlalchemy_dqlite-0.3.0/tests/test_savepoint_leader_flip_loud_failure.py +86 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_set_isolation_level_case_insensitive.py +8 -29
- sqlalchemy_dqlite-0.3.0/tests/test_set_isolation_level_none_rejected.py +30 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_stream_results_documented_eager_buffer.py +4 -27
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_submodule_all.py +2 -7
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_sync_connect_creator_fn_shape.py +2 -5
- sqlalchemy_dqlite-0.3.0/tests/test_sync_do_rollback_transport_fault_classified.py +63 -0
- sqlalchemy_dqlite-0.3.0/tests/test_sync_do_terminate.py +48 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_sync_do_terminate_log_includes_peer_and_id.py +2 -7
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_time_colspec.py +2 -7
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_time_tz_conversion.py +4 -16
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_transitive_wire_import.py +2 -8
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_transport_class_exceptions_consolidation.py +24 -87
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_url_int_upper_boundary_off_by_one.py +6 -28
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_url_max_rows_none_token.py +4 -14
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_url_pre_validation.py +8 -22
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_url_timeout_validators_share_ssot.py +6 -25
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_walk_cause_chain_caps_pathological_group_size.py +4 -28
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_walk_cause_chain_cycle_at_depth.py +1 -10
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_walk_cause_chain_depth_budget.py +7 -19
- sqlalchemy_dqlite-0.3.0/tests/test_wire_decode_failed_prefix_constant_used_by_dialect.py +15 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/uv.lock +4 -4
- sqlalchemy_dqlite-0.2.2/PKG-INFO +0 -185
- sqlalchemy_dqlite-0.2.2/README.md +0 -149
- sqlalchemy_dqlite-0.2.2/src/sqlalchemydqlite/aio.py +0 -2420
- sqlalchemy_dqlite-0.2.2/src/sqlalchemydqlite/base.py +0 -3745
- sqlalchemy_dqlite-0.2.2/src/sqlalchemydqlite/provision.py +0 -688
- sqlalchemy_dqlite-0.2.2/src/sqlalchemydqlite/requirements.py +0 -740
- sqlalchemy_dqlite-0.2.2/tests/compliance/conftest.py +0 -107
- sqlalchemy_dqlite-0.2.2/tests/compliance/test_suite.py +0 -19
- sqlalchemy_dqlite-0.2.2/tests/conftest.py +0 -97
- sqlalchemy_dqlite-0.2.2/tests/integration/test_isolation_level_pin.py +0 -56
- sqlalchemy_dqlite-0.2.2/tests/test_adapter_fetchmany_negative.py +0 -57
- sqlalchemy_dqlite-0.2.2/tests/test_aio_adapted_connection_public_paths.py +0 -157
- sqlalchemy_dqlite-0.2.2/tests/test_aio_async_creator_fn_shape_check.py +0 -75
- sqlalchemy_dqlite-0.2.2/tests/test_aio_await_staticmethod.py +0 -32
- sqlalchemy_dqlite-0.2.2/tests/test_aio_close_rollback_runtime_error_case_insensitive.py +0 -64
- sqlalchemy_dqlite-0.2.2/tests/test_aio_close_suppress_and_soft_close_noop.py +0 -117
- sqlalchemy_dqlite-0.2.2/tests/test_aio_close_terminate_in_greenlet_preflight.py +0 -63
- sqlalchemy_dqlite-0.2.2/tests/test_aio_close_terminate_outside_greenlet.py +0 -173
- sqlalchemy_dqlite-0.2.2/tests/test_aio_cursor_class_hooks.py +0 -103
- sqlalchemy_dqlite-0.2.2/tests/test_aio_dialect_do_close_releases_inner_strong_ref.py +0 -99
- sqlalchemy_dqlite-0.2.2/tests/test_aio_do_close_fallback_force_close_transport.py +0 -109
- sqlalchemy_dqlite-0.2.2/tests/test_aio_force_close_transport_cancelled_error.py +0 -67
- sqlalchemy_dqlite-0.2.2/tests/test_aio_force_close_transport_dead_proxy.py +0 -104
- sqlalchemy_dqlite-0.2.2/tests/test_aio_force_close_transport_docstring_pins.py +0 -61
- sqlalchemy_dqlite-0.2.2/tests/test_aio_handle_exception_delegates_to_remap_helper.py +0 -199
- sqlalchemy_dqlite-0.2.2/tests/test_aio_no_in_function_imports.py +0 -42
- sqlalchemy_dqlite-0.2.2/tests/test_aio_post_close_proxy_guards.py +0 -166
- sqlalchemy_dqlite-0.2.2/tests/test_async_adapted_connection_init_positional_swap_guard.py +0 -76
- sqlalchemy_dqlite-0.2.2/tests/test_async_adapted_connection_init_sa_reference_signature.py +0 -59
- sqlalchemy_dqlite-0.2.2/tests/test_async_adapted_connection_init_three_call_shapes.py +0 -64
- sqlalchemy_dqlite-0.2.2/tests/test_async_adapted_cursor_doc.py +0 -69
- sqlalchemy_dqlite-0.2.2/tests/test_async_adapter_op_lock_serialisation.py +0 -102
- sqlalchemy_dqlite-0.2.2/tests/test_async_adapter_terminate_handled_exceptions.py +0 -36
- sqlalchemy_dqlite-0.2.2/tests/test_async_commit_cancel_invalidates_slot.py +0 -126
- sqlalchemy_dqlite-0.2.2/tests/test_async_creator_fn_noncallable_rejected.py +0 -31
- sqlalchemy_dqlite-0.2.2/tests/test_async_cursor_after_connection_close.py +0 -51
- sqlalchemy_dqlite-0.2.2/tests/test_async_cursor_sync_only_context_manager.py +0 -51
- sqlalchemy_dqlite-0.2.2/tests/test_async_isolation_level.py +0 -44
- sqlalchemy_dqlite-0.2.2/tests/test_colspecs_pysqlite_time_inherited_baseline.py +0 -29
- sqlalchemy_dqlite-0.2.2/tests/test_cross_package_version_parity.py +0 -137
- sqlalchemy_dqlite-0.2.2/tests/test_datetime_bind_widens_date.py +0 -57
- sqlalchemy_dqlite-0.2.2/tests/test_ddl_kwarg_prefix_documented.py +0 -68
- sqlalchemy_dqlite-0.2.2/tests/test_description_alias_shared.py +0 -32
- sqlalchemy_dqlite-0.2.2/tests/test_detect_autocommit_setting.py +0 -102
- sqlalchemy_dqlite-0.2.2/tests/test_dial_attempt_timeout_url_and_connect_args.py +0 -75
- sqlalchemy_dqlite-0.2.2/tests/test_dialect_docstring_pins.py +0 -81
- sqlalchemy_dqlite-0.2.2/tests/test_dialect_final_annotated.py +0 -40
- sqlalchemy_dqlite-0.2.2/tests/test_dialect_set_isolation_level_annotation.py +0 -35
- sqlalchemy_dqlite-0.2.2/tests/test_disconnect_substring_producer_emission.py +0 -199
- sqlalchemy_dqlite-0.2.2/tests/test_do_begin_savepoint_asymmetry_documented.py +0 -58
- sqlalchemy_dqlite-0.2.2/tests/test_do_close_calls_dbapi_close_directly.py +0 -137
- sqlalchemy_dqlite-0.2.2/tests/test_do_close_fallback_uses_sync_transport_teardown.py +0 -94
- sqlalchemy_dqlite-0.2.2/tests/test_do_execute_local_override_pin.py +0 -74
- sqlalchemy_dqlite-0.2.2/tests/test_do_executemany_local_override_pin.py +0 -52
- sqlalchemy_dqlite-0.2.2/tests/test_do_ping_cursor_call_disconnect.py +0 -92
- sqlalchemy_dqlite-0.2.2/tests/test_do_recover_twophase_noreturn_annotation.py +0 -41
- sqlalchemy_dqlite-0.2.2/tests/test_dqlite_time_bind_processor_combined_fixes.py +0 -70
- sqlalchemy_dqlite-0.2.2/tests/test_dqlitetime_bind_processor_returns_none.py +0 -41
- sqlalchemy_dqlite-0.2.2/tests/test_drift_defence_pins.py +0 -90
- sqlalchemy_dqlite-0.2.2/tests/test_drop_user_tables_log_record_bounded.py +0 -52
- sqlalchemy_dqlite-0.2.2/tests/test_handle_exception_must_raise.py +0 -57
- sqlalchemy_dqlite-0.2.2/tests/test_handle_exception_two_pass_recovery_after_mixed_cancel_group.py +0 -74
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_bare_databaseerror_type_identity.py +0 -122
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_bare_dbe_short_circuits_on_slot_fatal_codes.py +0 -115
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_closed_handle_with_id_suffix.py +0 -99
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_database_error_substring.py +0 -108
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_event_loop_already_running_prefix.py +0 -53
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_event_loop_mismatch_substring.py +0 -78
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_oe_code_restriction.py +0 -37
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_protocol_error_arm.py +0 -69
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_raw_message.py +0 -80
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_recognises_connection_invalidated_interfaceerror.py +0 -96
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_recognises_cross_loop_interfaceerror.py +0 -114
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_recognises_used_after_fork.py +0 -63
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_substring_visible_within_dbapi_4kib_cap.py +0 -56
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_untracked_savepoint_negative.py +0 -84
- sqlalchemy_dqlite-0.2.2/tests/test_isolation_lookup_vs_values_list_divergence_documented.py +0 -95
- sqlalchemy_dqlite-0.2.2/tests/test_leader_error_codes_identity.py +0 -16
- sqlalchemy_dqlite-0.2.2/tests/test_log_safe_peer_helper.py +0 -70
- sqlalchemy_dqlite-0.2.2/tests/test_log_safe_peer_routing.py +0 -95
- sqlalchemy_dqlite-0.2.2/tests/test_log_safe_peer_uses_sanitize_for_log.py +0 -52
- sqlalchemy_dqlite-0.2.2/tests/test_phantom_returning_flag_does_not_exist_in_sa.py +0 -42
- sqlalchemy_dqlite-0.2.2/tests/test_provision_session_token_shape.py +0 -72
- sqlalchemy_dqlite-0.2.2/tests/test_raft_collapse_markers_upstream_provenance.py +0 -55
- sqlalchemy_dqlite-0.2.2/tests/test_remap_loop_state_helper_docstring_pin.py +0 -33
- sqlalchemy_dqlite-0.2.2/tests/test_requirements_closed_marker_present.py +0 -54
- sqlalchemy_dqlite-0.2.2/tests/test_requirements_recently_opened.py +0 -46
- sqlalchemy_dqlite-0.2.2/tests/test_reset_isolation_level_is_local_noop.py +0 -58
- sqlalchemy_dqlite-0.2.2/tests/test_result_processor_edge_branches.py +0 -58
- sqlalchemy_dqlite-0.2.2/tests/test_savepoint_leader_flip_loud_failure.py +0 -138
- sqlalchemy_dqlite-0.2.2/tests/test_set_isolation_level_none_rejected.py +0 -48
- sqlalchemy_dqlite-0.2.2/tests/test_sync_do_rollback_transport_fault_classified.py +0 -98
- sqlalchemy_dqlite-0.2.2/tests/test_sync_do_terminate.py +0 -67
- sqlalchemy_dqlite-0.2.2/tests/test_wire_decode_failed_prefix_constant_used_by_dialect.py +0 -25
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/.gitignore +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/LICENSE.md +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/scripts/check-commit-msg.sh +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/setup.cfg +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/src/sqlalchemydqlite/py.typed +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/__init__.py +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/integration/__init__.py +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_async_ping_single_execute_no_fetchone.py +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_top_level_logger_has_null_handler.py +0 -0
- {sqlalchemy_dqlite-0.2.2 → sqlalchemy_dqlite-0.3.0}/tests/test_version.py +0 -0
|
@@ -79,13 +79,22 @@ suite explicitly with `pytest tests/compliance/`.
|
|
|
79
79
|
### Why ~700 suite tests are skipped (and that's correct)
|
|
80
80
|
|
|
81
81
|
Each compliance run reports a large `skipped` count (currently
|
|
82
|
-
~
|
|
82
|
+
~639). Most skips are gated by a `Requirements.<feature>` declaration
|
|
83
83
|
in `src/sqlalchemydqlite/requirements.py` that says "dqlite doesn't
|
|
84
84
|
support this." The skipped tests are testing capabilities the
|
|
85
85
|
underlying database genuinely lacks — running them would fail with
|
|
86
86
|
`syntax error` / `unknown database` / `no such function`, not with
|
|
87
87
|
real bugs.
|
|
88
88
|
|
|
89
|
+
A few schema-using parametrize variants are skipped at the
|
|
90
|
+
compliance-conftest level instead (`_SCHEMA_USING_PARAMETRIZE_SKIPS`
|
|
91
|
+
in `tests/compliance/conftest.py`): SQLAlchemy parametrises some
|
|
92
|
+
reflection tests with `use_schema=True/False` without gating the
|
|
93
|
+
`True` branch on `requires.schemas`, so the `True` variants try to
|
|
94
|
+
create tables in `test_schema` (which dqlite lacks, having no ATTACH)
|
|
95
|
+
and are skipped explicitly while the `False` variants run for real
|
|
96
|
+
coverage.
|
|
97
|
+
|
|
89
98
|
Examples of features dqlite doesn't have (each declared
|
|
90
99
|
`exclusions.closed()`):
|
|
91
100
|
|
|
@@ -141,6 +150,31 @@ the cluster.
|
|
|
141
150
|
.venv/bin/mypy src
|
|
142
151
|
```
|
|
143
152
|
|
|
153
|
+
## Comments and docstrings
|
|
154
|
+
|
|
155
|
+
Keep comments and docstrings to a minimum — the code should be clear
|
|
156
|
+
enough to stand on its own. Prefer renaming a function or variable over
|
|
157
|
+
writing a comment to explain an unclear one.
|
|
158
|
+
|
|
159
|
+
- Write a comment only when it captures something genuinely non-obvious
|
|
160
|
+
that the code cannot: a subtle invariant, a security caveat, a
|
|
161
|
+
workaround for an upstream bug, or a "why" a reader would otherwise
|
|
162
|
+
get wrong. Delete comments that merely restate what the code does.
|
|
163
|
+
- Most functions need no docstring. Add a one-line docstring only when
|
|
164
|
+
it tells a reader something the name and signature do not. Avoid
|
|
165
|
+
multi-paragraph essays, param-by-param prose, and "Notes/Divergence"
|
|
166
|
+
sections.
|
|
167
|
+
- Do not record rationale, history, or decision logs in comments — that
|
|
168
|
+
context belongs in the commit message and git history (`git blame`,
|
|
169
|
+
`git log -p`), where it stays attached to the change instead of aging
|
|
170
|
+
in the source.
|
|
171
|
+
- Tooling directives (`# type:`, `# noqa`, `# pragma:`) are exempt —
|
|
172
|
+
keep them.
|
|
173
|
+
|
|
174
|
+
When in doubt, leave it out: a missing explanation is a `git blame`
|
|
175
|
+
away; a redundant or stale comment is noise every future reader pays
|
|
176
|
+
for.
|
|
177
|
+
|
|
144
178
|
## Pre-commit Workflow
|
|
145
179
|
|
|
146
180
|
```bash
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sqlalchemy-dqlite
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: SQLAlchemy 2.0 dialect for dqlite distributed SQLite
|
|
5
|
+
Project-URL: Homepage, https://github.com/letsdiscodev/sqlalchemy-dqlite
|
|
6
|
+
Project-URL: Repository, https://github.com/letsdiscodev/sqlalchemy-dqlite
|
|
7
|
+
Project-URL: Issues, https://github.com/letsdiscodev/sqlalchemy-dqlite/issues
|
|
8
|
+
Author-email: Antoine Leclair <antoineleclair@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE.md
|
|
11
|
+
Keywords: database,distributed,dqlite,orm,sqlalchemy,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.4.0,>=0.3.0
|
|
26
|
+
Requires-Dist: dqlite-dbapi<0.4.0,>=0.3.0
|
|
27
|
+
Requires-Dist: dqlite-wire<0.4.0,>=0.3.0
|
|
28
|
+
Requires-Dist: sqlalchemy<3.0,>=2.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# sqlalchemy-dqlite
|
|
38
|
+
|
|
39
|
+
A [SQLAlchemy 2.0](https://www.sqlalchemy.org/) dialect for
|
|
40
|
+
[dqlite](https://dqlite.io/), Canonical's distributed SQLite. Point
|
|
41
|
+
SQLAlchemy (and Alembic, and most ORMs built on it) at a dqlite cluster
|
|
42
|
+
with a `dqlite://` URL and use it like any other backend.
|
|
43
|
+
|
|
44
|
+
It supports both the synchronous and the async SQLAlchemy engines, and
|
|
45
|
+
builds on dqlite's SQLite roots, so SQLAlchemy treats it as a SQLite-family
|
|
46
|
+
dialect.
|
|
47
|
+
|
|
48
|
+
## Is this the package you want?
|
|
49
|
+
|
|
50
|
+
Yes, if you use SQLAlchemy or an ORM/migration tool on top of it (Alembic,
|
|
51
|
+
Flask-SQLAlchemy, etc.) and want it backed by dqlite. If you want a plain
|
|
52
|
+
database driver without SQLAlchemy, use
|
|
53
|
+
[dqlite-dbapi](https://github.com/letsdiscodev/python-dqlite-dbapi).
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install sqlalchemy-dqlite
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Requires Python 3.13+ and SQLAlchemy 2.0+.
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from sqlalchemy import create_engine, text
|
|
67
|
+
|
|
68
|
+
engine = create_engine("dqlite://localhost:9001/mydb")
|
|
69
|
+
with engine.connect() as conn:
|
|
70
|
+
print(conn.execute(text("SELECT 1")).fetchone())
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Async:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
77
|
+
|
|
78
|
+
engine = create_async_engine("dqlite+aio://localhost:9001/mydb")
|
|
79
|
+
async with engine.connect() as conn:
|
|
80
|
+
result = await conn.execute(text("SELECT 1"))
|
|
81
|
+
print(result.fetchone())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
URL format: `dqlite://host:port/database` (or `dqlite+aio://…` for async).
|
|
85
|
+
The host:port is the bootstrap node — the cluster is discovered from there.
|
|
86
|
+
See [Connection URL](docs/connection-url.md) for details.
|
|
87
|
+
|
|
88
|
+
## The dqlite Python stack
|
|
89
|
+
|
|
90
|
+
This is the top of four layered packages. Each builds on the one below:
|
|
91
|
+
|
|
92
|
+
| Package | Role |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| **sqlalchemy-dqlite** — this package | SQLAlchemy 2.0 dialect |
|
|
95
|
+
| [dqlite-dbapi](https://github.com/letsdiscodev/python-dqlite-dbapi) | PEP 249 (DB-API 2.0) driver — sync & async |
|
|
96
|
+
| [dqlite-client](https://github.com/letsdiscodev/python-dqlite-client) | Async wire client — pooling, leader discovery |
|
|
97
|
+
| [dqlite-wire](https://github.com/letsdiscodev/python-dqlite-wire) | Wire-protocol codec |
|
|
98
|
+
|
|
99
|
+
## Documentation
|
|
100
|
+
|
|
101
|
+
- [Transactions](docs/transactions.md) — SQLAlchemy owns `BEGIN`/`COMMIT`; no `AUTOCOMMIT`.
|
|
102
|
+
- [Connection URL](docs/connection-url.md) — URL format, the bootstrap address, and query parameters.
|
|
103
|
+
- [Limitations & notes](docs/limitations.md) — STRICT tables, savepoint
|
|
104
|
+
naming, and a server-version NULL gotcha.
|
|
105
|
+
|
|
106
|
+
## Development
|
|
107
|
+
|
|
108
|
+
See [DEVELOPMENT.md](DEVELOPMENT.md) for setup, contribution guidelines, and
|
|
109
|
+
how to run the SQLAlchemy compliance test suite.
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# sqlalchemy-dqlite
|
|
2
|
+
|
|
3
|
+
A [SQLAlchemy 2.0](https://www.sqlalchemy.org/) dialect for
|
|
4
|
+
[dqlite](https://dqlite.io/), Canonical's distributed SQLite. Point
|
|
5
|
+
SQLAlchemy (and Alembic, and most ORMs built on it) at a dqlite cluster
|
|
6
|
+
with a `dqlite://` URL and use it like any other backend.
|
|
7
|
+
|
|
8
|
+
It supports both the synchronous and the async SQLAlchemy engines, and
|
|
9
|
+
builds on dqlite's SQLite roots, so SQLAlchemy treats it as a SQLite-family
|
|
10
|
+
dialect.
|
|
11
|
+
|
|
12
|
+
## Is this the package you want?
|
|
13
|
+
|
|
14
|
+
Yes, if you use SQLAlchemy or an ORM/migration tool on top of it (Alembic,
|
|
15
|
+
Flask-SQLAlchemy, etc.) and want it backed by dqlite. If you want a plain
|
|
16
|
+
database driver without SQLAlchemy, use
|
|
17
|
+
[dqlite-dbapi](https://github.com/letsdiscodev/python-dqlite-dbapi).
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install sqlalchemy-dqlite
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requires Python 3.13+ and SQLAlchemy 2.0+.
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from sqlalchemy import create_engine, text
|
|
31
|
+
|
|
32
|
+
engine = create_engine("dqlite://localhost:9001/mydb")
|
|
33
|
+
with engine.connect() as conn:
|
|
34
|
+
print(conn.execute(text("SELECT 1")).fetchone())
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Async:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
41
|
+
|
|
42
|
+
engine = create_async_engine("dqlite+aio://localhost:9001/mydb")
|
|
43
|
+
async with engine.connect() as conn:
|
|
44
|
+
result = await conn.execute(text("SELECT 1"))
|
|
45
|
+
print(result.fetchone())
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
URL format: `dqlite://host:port/database` (or `dqlite+aio://…` for async).
|
|
49
|
+
The host:port is the bootstrap node — the cluster is discovered from there.
|
|
50
|
+
See [Connection URL](docs/connection-url.md) for details.
|
|
51
|
+
|
|
52
|
+
## The dqlite Python stack
|
|
53
|
+
|
|
54
|
+
This is the top of four layered packages. Each builds on the one below:
|
|
55
|
+
|
|
56
|
+
| Package | Role |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| **sqlalchemy-dqlite** — this package | SQLAlchemy 2.0 dialect |
|
|
59
|
+
| [dqlite-dbapi](https://github.com/letsdiscodev/python-dqlite-dbapi) | PEP 249 (DB-API 2.0) driver — sync & async |
|
|
60
|
+
| [dqlite-client](https://github.com/letsdiscodev/python-dqlite-client) | Async wire client — pooling, leader discovery |
|
|
61
|
+
| [dqlite-wire](https://github.com/letsdiscodev/python-dqlite-wire) | Wire-protocol codec |
|
|
62
|
+
|
|
63
|
+
## Documentation
|
|
64
|
+
|
|
65
|
+
- [Transactions](docs/transactions.md) — SQLAlchemy owns `BEGIN`/`COMMIT`; no `AUTOCOMMIT`.
|
|
66
|
+
- [Connection URL](docs/connection-url.md) — URL format, the bootstrap address, and query parameters.
|
|
67
|
+
- [Limitations & notes](docs/limitations.md) — STRICT tables, savepoint
|
|
68
|
+
naming, and a server-version NULL gotcha.
|
|
69
|
+
|
|
70
|
+
## Development
|
|
71
|
+
|
|
72
|
+
See [DEVELOPMENT.md](DEVELOPMENT.md) for setup, contribution guidelines, and
|
|
73
|
+
how to run the SQLAlchemy compliance test suite.
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
MIT
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Connection URL
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
dqlite://host:port/database
|
|
5
|
+
dqlite+aio://host:port/database # async engine
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
from sqlalchemy import create_engine
|
|
10
|
+
engine = create_engine("dqlite://localhost:9001/mydb")
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## The host:port is a bootstrap address
|
|
14
|
+
|
|
15
|
+
The host:port pair is just the *bootstrap* node. The dqlite client connects
|
|
16
|
+
there, reads that node's leader-info response, and discovers the rest of the
|
|
17
|
+
cluster from it. You point at one reachable node, not the whole cluster.
|
|
18
|
+
|
|
19
|
+
If that host is unreachable, leader discovery cannot start. Multi-address
|
|
20
|
+
bootstrap is not exposed at the dialect URL surface — if you want
|
|
21
|
+
bootstrap-from-many, put a load balancer or DNS round-robin in front of the
|
|
22
|
+
cluster, or rotate the URL host across deployments.
|
|
23
|
+
|
|
24
|
+
## Query parameters
|
|
25
|
+
|
|
26
|
+
Connection options can be passed as URL query parameters (e.g.
|
|
27
|
+
`max_total_rows`). When a parameter is repeated
|
|
28
|
+
(`?max_total_rows=100&max_total_rows=200`), the **last** occurrence wins —
|
|
29
|
+
matching `urllib.parse.parse_qsl` ordering. Templated URLs that layer values
|
|
30
|
+
from multiple sources should be aware that a duplicated key silently
|
|
31
|
+
overrides the earlier value rather than raising.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Limitations & notes
|
|
2
|
+
|
|
3
|
+
## STRICT tables are not emitted by the compiler
|
|
4
|
+
|
|
5
|
+
SQLAlchemy's SQLite dialect gates `CREATE TABLE ... STRICT` compilation on
|
|
6
|
+
`server_version_info >= (3, 37)`. This dialect pins
|
|
7
|
+
`sqlite_version_info = (3, 35, 0)` as a documented floor so it doesn't reject
|
|
8
|
+
connections to older-server clusters. As a result, SQLAlchemy's compiler will
|
|
9
|
+
**not** emit STRICT DDL through this dialect, even when your cluster ships
|
|
10
|
+
SQLite 3.37+.
|
|
11
|
+
|
|
12
|
+
If your cluster supports STRICT tables and you want them, emit the DDL as raw
|
|
13
|
+
SQL (`engine.execute(text("CREATE TABLE ... STRICT"))`) rather than through
|
|
14
|
+
SQLAlchemy's `Table` / `Column` model.
|
|
15
|
+
|
|
16
|
+
## NULL in Boolean/DateTime columns depends on the server version
|
|
17
|
+
|
|
18
|
+
A 2026 dqlite server change (upstream commit `f30fc99`) changed how a NULL
|
|
19
|
+
cell is encoded in columns declared `BOOLEAN`, `DATE`, `DATETIME`, or
|
|
20
|
+
`TIMESTAMP`:
|
|
21
|
+
|
|
22
|
+
- **Before**: a NULL came back as `False` (Boolean) or `""` (datetime
|
|
23
|
+
types) — indistinguishable from a real value.
|
|
24
|
+
- **After**: a NULL comes back as `None`.
|
|
25
|
+
|
|
26
|
+
For ORM models this means a `Boolean()` / `DateTime()` column that used to
|
|
27
|
+
return `False` / `""` will start returning `None` after a server upgrade, and
|
|
28
|
+
`nullable=False` constraints may start tripping on rows that previously
|
|
29
|
+
decoded to non-NULL. There is no handshake distinguishing the two server
|
|
30
|
+
versions — check your dqlite cluster version before relying on the post-change
|
|
31
|
+
semantics.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Transactions
|
|
2
|
+
|
|
3
|
+
SQLAlchemy owns `BEGIN` / `COMMIT` / `ROLLBACK` for any block opened via
|
|
4
|
+
`engine.begin()`, `connection.begin()`, or `session.begin()`. **Do not issue
|
|
5
|
+
a raw `BEGIN` yourself** — this is the standard SQLAlchemy model, and it is
|
|
6
|
+
the same on every backend (pysqlite, PostgreSQL, MySQL).
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
from sqlalchemy import create_engine, text
|
|
10
|
+
|
|
11
|
+
engine = create_engine("dqlite://localhost:9001/mydb")
|
|
12
|
+
|
|
13
|
+
# OK — SQLAlchemy emits BEGIN / COMMIT for you:
|
|
14
|
+
with engine.begin() as conn:
|
|
15
|
+
conn.execute(text("INSERT INTO t VALUES (1)"))
|
|
16
|
+
|
|
17
|
+
# WRONG — a manual BEGIN inside an SA-managed transaction errors with
|
|
18
|
+
# "cannot start a transaction within a transaction":
|
|
19
|
+
with engine.begin() as conn:
|
|
20
|
+
conn.execute(text("BEGIN")) # error
|
|
21
|
+
conn.execute(text("INSERT INTO t VALUES (1)"))
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The same applies to `engine.connect()`: SQLAlchemy auto-begins on the first
|
|
25
|
+
execute, so a user-issued `text("BEGIN")` collides the same way.
|
|
26
|
+
|
|
27
|
+
## No `AUTOCOMMIT`
|
|
28
|
+
|
|
29
|
+
`isolation_level="AUTOCOMMIT"` is rejected. Every dqlite statement goes
|
|
30
|
+
through Raft consensus and there is no per-statement autocommit mode — use
|
|
31
|
+
`engine.begin()` (or `connection.begin()`) for writes. Isolation is always
|
|
32
|
+
SERIALIZABLE.
|
|
33
|
+
|
|
34
|
+
See SQLAlchemy's
|
|
35
|
+
[transaction docs](https://docs.sqlalchemy.org/en/20/core/connections.html#using-transactions)
|
|
36
|
+
for the full model.
|
|
37
|
+
|
|
38
|
+
## Savepoints
|
|
39
|
+
|
|
40
|
+
SQLAlchemy's `Session.begin_nested()` / `connection.begin_nested()` use
|
|
41
|
+
generated savepoint names (e.g. `sa_savepoint_1`), which the dialect tracks
|
|
42
|
+
correctly — no action needed.
|
|
43
|
+
|
|
44
|
+
Only **raw** SQL with an unusual savepoint name (quoted, backticked,
|
|
45
|
+
bracketed, unicode, or leading-digit, e.g. `text('SAVEPOINT "weird name"')`)
|
|
46
|
+
falls outside the tracker. When that happens the connection is conservatively
|
|
47
|
+
flagged as carrying an untracked savepoint, and SQLAlchemy issues a safety
|
|
48
|
+
`ROLLBACK` on the next pool checkin — one extra round-trip per checkout for
|
|
49
|
+
the rest of that connection's life in the pool. Stick to bare-ASCII savepoint
|
|
50
|
+
names in raw SQL to avoid it.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sqlalchemy-dqlite"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
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.
|
|
29
|
+
"dqlite-dbapi>=0.3.0,<0.4.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.
|
|
37
|
-
"dqlite-wire>=0.
|
|
36
|
+
"dqlite-client>=0.3.0,<0.4.0",
|
|
37
|
+
"dqlite-wire>=0.3.0,<0.4.0",
|
|
38
38
|
"sqlalchemy>=2.0,<3.0",
|
|
39
39
|
]
|
|
40
40
|
|
|
@@ -27,19 +27,14 @@ 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.
|
|
30
|
+
__version__: _Final[str] = "0.3.0"
|
|
31
31
|
|
|
32
|
-
# SA convention
|
|
33
|
-
#
|
|
34
|
-
# default sync class. Pysqlite at ``.venv/.../sqlite/__init__.py:34``
|
|
35
|
-
# does ``base.dialect = dialect = pysqlite.dialect``; we follow the same
|
|
36
|
-
# pattern so ``from sqlalchemydqlite import dialect`` resolves
|
|
37
|
-
# canonically.
|
|
32
|
+
# SA convention: every shipped dialect package exposes ``<package>.dialect``
|
|
33
|
+
# pointing at the default sync class.
|
|
38
34
|
dialect: _Final[type[DqliteDialect]] = DqliteDialect
|
|
39
35
|
dialect_aio: _Final[type[DqliteDialect_aio]] = DqliteDialect_aio
|
|
40
36
|
|
|
41
|
-
__all__ = [
|
|
42
|
-
# Dialect entry points
|
|
37
|
+
__all__ = [
|
|
43
38
|
"DqliteCompiler",
|
|
44
39
|
"DqliteDialect",
|
|
45
40
|
"DqliteDialect_aio",
|
|
@@ -48,7 +43,6 @@ __all__ = [ # grouped: dialect entry points, then SA-shared SQLite types
|
|
|
48
43
|
"dialect",
|
|
49
44
|
"dialect_aio",
|
|
50
45
|
"insert",
|
|
51
|
-
# SA-shared SQLite type re-exports (alphabetical within group)
|
|
52
46
|
"BLOB",
|
|
53
47
|
"BOOLEAN",
|
|
54
48
|
"CHAR",
|
|
@@ -67,9 +61,5 @@ __all__ = [ # grouped: dialect entry points, then SA-shared SQLite types
|
|
|
67
61
|
"VARCHAR",
|
|
68
62
|
]
|
|
69
63
|
|
|
70
|
-
#
|
|
71
|
-
# to the library's top-level logger so applications that have not
|
|
72
|
-
# configured logging don't see the ``lastResort`` stderr emission,
|
|
73
|
-
# and downstream code can silence the library cleanly via
|
|
74
|
-
# ``getLogger("sqlalchemydqlite").propagate = False``.
|
|
64
|
+
# NullHandler keeps the library quiet until the application configures logging.
|
|
75
65
|
logging.getLogger(__name__).addHandler(logging.NullHandler())
|