diff --git a/docs/manager/config.md b/docs/manager/config.md index 767ad6a..eb40290 100644 --- a/docs/manager/config.md +++ b/docs/manager/config.md @@ -88,3 +88,23 @@ async with sa_manager.get_session() as session: Note that async implementation has several differences from the sync one, make sure to check [SQLAlchemy asyncio documentation](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) + +## Bind engines lifecycle + +Engine disposal is handled automatically by `SQLAlchemyBindManager`. Engines are disposed when: + +* The manager instance is garbage collected +* The Python interpreter shuts down (via an `atexit` handler) + +In some scenarios, such as automated tests, you may need to manually dispose engines to release database connections between tests. The `dispose_engines()` method is available for this purpose: + +```python +sa_manager = SQLAlchemyBindManager(config) + +# ... use the manager ... + +# Manually dispose all engines +sa_manager.dispose_engines() +``` + +This method disposes all engines synchronously, including async engines (using their underlying sync engine). diff --git a/sqlalchemy_bind_manager/_bind_manager.py b/sqlalchemy_bind_manager/_bind_manager.py index ae89e59..a8cb57e 100644 --- a/sqlalchemy_bind_manager/_bind_manager.py +++ b/sqlalchemy_bind_manager/_bind_manager.py @@ -91,7 +91,7 @@ def __init__( self.__init_bind(DEFAULT_BIND_NAME, config) SQLAlchemyBindManager._instances.add(self) - def _dispose_sync(self) -> None: + def dispose_engines(self) -> None: """Dispose all engines synchronously. This method is safe to call from any context, including __del__ @@ -105,7 +105,7 @@ def _dispose_sync(self) -> None: bind.engine.dispose() def __del__(self) -> None: - self._dispose_sync() + self.dispose_engines() def __init_bind(self, name: str, config: SQLAlchemyConfig): if not isinstance(config, SQLAlchemyConfig): @@ -232,4 +232,4 @@ def _cleanup_all_managers() -> None: called yet due to reference cycles or other GC timing issues. """ for manager in list(SQLAlchemyBindManager._instances): - manager._dispose_sync() + manager.dispose_engines() diff --git a/tests/test_sqlalchemy_bind_manager.py b/tests/test_sqlalchemy_bind_manager.py index 80eb64f..27b582f 100644 --- a/tests/test_sqlalchemy_bind_manager.py +++ b/tests/test_sqlalchemy_bind_manager.py @@ -115,8 +115,8 @@ def test_atexit_cleanup_disposes_all_managers(multiple_config): with patch.object( sa_manager, - "_dispose_sync", - ) as mocked_dispose_sync: + "dispose_engines", + ) as mocked_dispose_engines: _cleanup_all_managers() - mocked_dispose_sync.assert_called_once() + mocked_dispose_engines.assert_called_once()