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.
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/PKG-INFO +4 -4
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/pyproject.toml +4 -4
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/__init__.py +1 -1
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/aio.py +198 -45
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/base.py +847 -94
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/provision.py +214 -44
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/requirements.py +274 -18
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/conftest.py +17 -0
- sqlalchemy_dqlite-0.2.2/tests/integration/test_concurrent_writers_begin_immediate.py +92 -0
- sqlalchemy_dqlite-0.2.2/tests/integration/test_dqlite_session_mode_execution_option.py +284 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_engine_begin_is_atomic.py +12 -5
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_pool_recycle_and_dispose_fan_out.py +24 -6
- sqlalchemy_dqlite-0.2.2/tests/integration/test_temp_table_scoping_per_connection.py +69 -0
- sqlalchemy_dqlite-0.2.2/tests/test_aio_async_ping_closed_proxy_log.py +63 -0
- sqlalchemy_dqlite-0.2.2/tests/test_aio_force_close_transport_cancelled_error.py +67 -0
- sqlalchemy_dqlite-0.2.2/tests/test_aio_handle_exception_group_single_child_unwrap.py +76 -0
- sqlalchemy_dqlite-0.2.2/tests/test_async_adapted_connection_init_positional_swap_guard.py +76 -0
- sqlalchemy_dqlite-0.2.2/tests/test_async_adapter_terminate_handled_exceptions_runtimeerror.py +38 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_dialect_isolation_set.py +11 -8
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_bind_side_cross_type_rejection.py +6 -1
- sqlalchemy_dqlite-0.2.2/tests/test_busy_timeout_url_and_connect_args.py +138 -0
- sqlalchemy_dqlite-0.2.2/tests/test_check_same_thread_url_and_connect_args.py +177 -0
- sqlalchemy_dqlite-0.2.2/tests/test_colspecs_pysqlite_time_inherited_baseline.py +29 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_datetime_bind_widens_date.py +14 -4
- sqlalchemy_dqlite-0.2.2/tests/test_ddl_kwarg_dqlite_prefix_rejected.py +67 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_ddl_kwarg_prefix_documented.py +19 -17
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect.py +16 -27
- sqlalchemy_dqlite-0.2.2/tests/test_dialect_set_isolation_level_annotation.py +35 -0
- sqlalchemy_dqlite-0.2.2/tests/test_do_begin_cursor_close_widened_catch.py +85 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_begin_emits_begin.py +14 -15
- sqlalchemy_dqlite-0.2.2/tests/test_do_execute_local_override_pin.py +74 -0
- sqlalchemy_dqlite-0.2.2/tests/test_do_recover_twophase_noreturn_annotation.py +41 -0
- sqlalchemy_dqlite-0.2.2/tests/test_do_terminate_narrow_catch_propagates_refactor_break.py +85 -0
- sqlalchemy_dqlite-0.2.2/tests/test_do_terminate_non_raising_contract_widened.py +131 -0
- sqlalchemy_dqlite-0.2.2/tests/test_dqlite_datetime_bind_zero_microseconds.py +69 -0
- sqlalchemy_dqlite-0.2.2/tests/test_dqlite_datetime_time_literal_processor_microseconds.py +70 -0
- sqlalchemy_dqlite-0.2.2/tests/test_dqlite_datetime_types_reject_pysqlite_kwargs.py +73 -0
- sqlalchemy_dqlite-0.2.2/tests/test_dqlite_time_bind_processor_combined_fixes.py +70 -0
- sqlalchemy_dqlite-0.2.2/tests/test_dqlite_time_result_processor_preserves_offset.py +66 -0
- sqlalchemy_dqlite-0.2.2/tests/test_dqlitetime_bind_processor_returns_none.py +41 -0
- sqlalchemy_dqlite-0.2.2/tests/test_dqlitetime_cross_axis_narrow_with_timezone_true.py +49 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_bare_databaseerror_type_identity.py +14 -7
- sqlalchemy_dqlite-0.2.2/tests/test_is_disconnect_bare_dbe_short_circuits_on_slot_fatal_codes.py +115 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_database_error_substring.py +13 -7
- sqlalchemy_dqlite-0.2.2/tests/test_provision_drop_user_tables_and_reap.py +301 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_drop_user_tables_log_sanitized.py +17 -8
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_drop_user_tables_quote_escaping.py +15 -5
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_generate_driver_url.py +16 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_result_processor_warning_one_shot.py +40 -7
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_set_isolation_level_case_insensitive.py +8 -5
- sqlalchemy_dqlite-0.2.2/tests/test_set_isolation_level_none_rejected.py +48 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_transport_class_exceptions_consolidation.py +9 -6
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/uv.lock +4 -4
- sqlalchemy_dqlite-0.2.0/tests/test_aio_force_close_transport_cancelled_error.py +0 -45
- sqlalchemy_dqlite-0.2.0/tests/test_dialect_set_isolation_level_annotation.py +0 -35
- sqlalchemy_dqlite-0.2.0/tests/test_do_terminate_narrow_catch_propagates_refactor_break.py +0 -65
- sqlalchemy_dqlite-0.2.0/tests/test_dqlitetime_bind_processor_returns_none.py +0 -35
- sqlalchemy_dqlite-0.2.0/tests/test_dqlitetime_cross_axis_narrow_with_timezone_true.py +0 -50
- sqlalchemy_dqlite-0.2.0/tests/test_provision_drop_user_tables_and_reap.py +0 -208
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/.github/workflows/publish-to-pypi.yml +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/.gitignore +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/DEVELOPMENT.md +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/LICENSE.md +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/README.md +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/scripts/check-commit-msg.sh +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/setup.cfg +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/src/sqlalchemydqlite/py.typed +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/__init__.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/compliance/conftest.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/compliance/test_suite.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/__init__.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/conftest.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_cancel_during_commit_invalidates_slot.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_cancel_inside_engine_begin.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_concurrent_sessions.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_context.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_executemany.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_async_savepoint_cancellation_invalidates_slot.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_bulk_dml_returning.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_date_tz_drop.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_dial_func_via_connect_args_reaches_dbapi.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_engine_events_fire.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_inspect_async_and_close_parity.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_isolation_level_pin.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_json_column.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_native_boolean.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_orm_operations.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_pool_pre_ping_leader_flip.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_pool_reset_on_return.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_reflect_column_types.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_savepoint_basic.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_savepoint_orm_session_begin_nested.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_savepoint_quoted_identifiers.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_time_and_reflection.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/integration/test_upsert.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_2pc_not_supported_error.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_arraysize_default_and_semantics.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_arraysize_validation.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_cursor_iterator_protocol.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_fetchmany_negative.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_handle_exception_hook.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_pickle_guards.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_slots.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_adapter_stale_rows.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_adapted_connection_public_paths.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_async_creator_fn.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_async_creator_fn_shape_check.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_await_staticmethod.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_final_close_suppression.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_loop_mismatch_remap.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_releases_inner_connection_pin.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_rollback_log.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_rollback_loop_already_running_remap.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_rollback_runtime_error_case_insensitive.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_suppress_and_soft_close_noop.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_cancellederror_force_close.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_in_greenlet_preflight.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_outside_greenlet.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_proxy_swap_arms.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_close_terminate_runtime_error_post_await.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_connect_construction_in_try_frame.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_connect_loop_state_remap.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_cursor_class_hooks.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_cursor_closed_before_server_side_precedence.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_cursor_time_error_remapped.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_dialect_do_close_releases_inner_strong_ref.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_close_fallback_force_close_transport.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_ping_cursor_construct_exception_arms.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_ping_override.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_ping_runtimeerror_returns_false.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_do_terminate_suppresses_exception.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_force_close_transport_dead_proxy.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_force_close_transport_docstring_pins.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_handle_exception_baseexceptiongroup_cancel_split.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_handle_exception_call_sites.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_handle_exception_delegates_to_remap_helper.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_is_disconnect_closed_proxy.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_no_in_function_imports.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_post_close_proxy_guards.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_terminate.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_aio_terminate_close_idempotence.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_cursor_closed_check.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_execute_handle_exception.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_execute_suppress_cancellederror.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_init_sa_reference_signature.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_connection_init_three_call_shapes.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_cursor_doc.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapted_cursor_sync_ctxmgr.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_autocommit_property.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_closed_checks.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_lastrowid_sticky.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_op_lock_serialisation.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_adapter_terminate_handled_exceptions.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_commit_cancel_invalidates_slot.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_creator_fn_noncallable_rejected.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_after_connection_close.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_arraysize_accepts_zero_and_negative.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_arraysize_propagates_to_underlying.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_closed_guard.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_closed_guards.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_execute_close_suppress_behavioural.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_executemany_listifies_iterable.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_handle_exception.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_cursor_sync_only_context_manager.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_executemany_leader_flip_state_reset.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_handle_exception_case_insensitive.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_handle_exception_cause_walk.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_isolation_level.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_ping_cancel_cleanup.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_ping_close_arm_narrow_vs_programmer_bug.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_ping_single_execute_no_fetchone.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_async_ping_translates_dead_proxy_to_interface_error.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_audit_2026_05_round2_sa_changes.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_audit_2026_05_sa_changes.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_audit_2026_05_sa_coverage.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_close_runs_close_after_rollback_raise.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_collated_coverage_gaps.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_compiler_regexp_match.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_compliance_conftest_staleness_pin.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_connect_args_kwarg_validation.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_connect_args_value_validators.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_cross_package_version_parity.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_cursor_close_narrow_suppress.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_datetime_date_parse_errors.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_datetime_time_cross_type_payload.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_datetime_tz_conversion.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_description_alias_shared.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_detect_autocommit_setting.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dial_attempt_timeout_url_and_connect_args.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_dialect_config.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_docstring_pins.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_final_annotated.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_inherits_pysqlite.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_init_eager_autocommit_rejection.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_init_preserves_pins.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_ipv6_url.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_sqlite_incompatibility_flags_pinned.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_dialect_url_credentials.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_disconnect_substring_producer_emission.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_begin_cancel.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_begin_savepoint_asymmetry_documented.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_close_calls_dbapi_close_directly.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_close_fallback_suppress_arm.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_close_fallback_uses_sync_transport_teardown.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_close_first_arm_absorbs_runtime_error.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_executemany_local_override_pin.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_catches_programming_error.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_close_narrow.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_cursor_call_disconnect.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_database_error.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_dialect_specific_select_one.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_interface_error_with_code.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_do_ping_non_disconnect_databaseerror_reraise.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_drift_defence_pins.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_drop_user_tables_log_record_bounded.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_floor_compile_time_gate.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_handle_exception_must_raise.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_handle_exception_remap.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_handle_exception_two_pass_recovery_after_mixed_cancel_group.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_bare_oserror_walked_not_early_return.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_case_insensitive.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_cause_walk.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_closed_handle_with_id_suffix.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_cluster_error.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_event_loop_already_running_prefix.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_event_loop_closed.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_event_loop_mismatch_substring.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_exception_group_and_substring_walk.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_interface_error_cause_walk.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_leader_lost_substring_case_insensitive.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_notfound_lookup_db.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_oe_code_restriction.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_protocol_error_arm.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_raft_collapse.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_raw_message.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_recognises_connection_invalidated_interfaceerror.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_recognises_cross_loop_interfaceerror.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_recognises_used_after_fork.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_substring_visible_within_dbapi_4kib_cap.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_untracked_savepoint_negative.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_is_disconnect_walks_cause_oserror.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_isolation_level_in_connect_args_directional_message.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_isolation_lookup_vs_values_list_divergence_documented.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_leader_error_codes_identity.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_log_safe_peer_helper.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_log_safe_peer_routing.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_log_safe_peer_uses_sanitize_for_log.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_max_message_size_url_and_connect_args.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_max_total_rows_warning_does_not_advertise_disabled_token.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_phantom_returning_flag_does_not_exist_in_sa.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_create_drop_db_log_sanitises_ident.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_format_url_ident_sanitisation.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_reap_dbs_info_log_credential_mask_and_sanitize.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_reap_dbs_log_sanitized.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_session_token_shape.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_provision_upsert.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_public_api.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_raft_collapse_markers_upstream_provenance.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_remap_loop_state_chains_from_matched_hop.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_remap_loop_state_helper_docstring_pin.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_requirements.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_requirements_closed_marker_present.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_requirements_recently_opened.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_reset_isolation_level_is_local_noop.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_result_processor_edge_branches.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_result_processor_log_payload_sanitized.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_result_processor_log_truncates_server_text.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_savepoint_leader_flip_loud_failure.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_stream_results_documented_eager_buffer.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_submodule_all.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_sync_connect_creator_fn_shape.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_sync_do_rollback_transport_fault_classified.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_sync_do_terminate.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_sync_do_terminate_log_includes_peer_and_id.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_time_colspec.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_time_tz_conversion.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_top_level_logger_has_null_handler.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_transitive_wire_import.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_url_int_upper_boundary_off_by_one.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_url_max_rows_none_token.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_url_pre_validation.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_url_timeout_validators_share_ssot.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_version.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_walk_cause_chain_caps_pathological_group_size.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_walk_cause_chain_cycle_at_depth.py +0 -0
- {sqlalchemy_dqlite-0.2.0 → sqlalchemy_dqlite-0.2.2}/tests/test_walk_cause_chain_depth_budget.py +0 -0
- {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.
|
|
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.
|
|
26
|
-
Requires-Dist: dqlite-dbapi<0.3.0,>=0.2.
|
|
27
|
-
Requires-Dist: dqlite-wire<0.3.0,>=0.2.
|
|
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.
|
|
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.
|
|
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.
|
|
37
|
-
"dqlite-wire>=0.2.
|
|
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.
|
|
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
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
``
|
|
940
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1498
|
-
self._handle_exception(exc)
|
|
1499
|
-
|
|
1500
|
-
|
|
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``
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
``
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
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
|
|
1663
|
-
# Narrow to ``
|
|
1664
|
-
#
|
|
1665
|
-
#
|
|
1666
|
-
#
|
|
1667
|
-
#
|
|
1668
|
-
#
|
|
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
|
-
#
|
|
1870
|
-
#
|
|
1871
|
-
#
|
|
1872
|
-
#
|
|
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``
|
|
2331
|
+
# ``inspect.iscoroutinefunction`` recognises
|
|
2185
2332
|
# ``functools.partial`` wrappers around an ``async def`` (a
|
|
2186
|
-
# legitimate user pattern).
|
|
2187
|
-
#
|
|
2188
|
-
#
|
|
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 "
|