feat: refactor database engine management with dual-backend support (…#613
Open
feat: refactor database engine management with dual-backend support (…#613
Conversation
…OEP + local egon-data) This commit introduces a comprehensive refactoring of how eDisGo manages database connections, enabling seamless switching between the OpenEnergyPlatform (OEP) and local egon-data PostgreSQL databases. ## Engine as EDisGo property (edisgo/edisgo.py) - Add lazy-initialized `engine` property to the EDisGo class, replacing the previous pattern of passing engine explicitly to every function call - Support multiple initialization modes via new constructor parameters: `engine`, `db_config_path`, `db_url`, `db_ssh`, `db_token` - Default to OEP engine when no configuration is provided - Add `__deepcopy__` method that excludes the unpicklable SQLAlchemy engine (contains _thread._local objects) and lets the copy lazily recreate its own connection ## Optional engine parameter in all DB functions Make the `engine` parameter optional (default=None) in all 33 functions across 8 modules that access the database. When engine is None, each function falls back to `edisgo_object.engine`. This maintains full backward compatibility — callers can still pass an explicit engine. Modified modules: - edisgo/io/timeseries_import.py (10 functions) - edisgo/io/electromobility_import.py (4 functions) - edisgo/io/dsm_import.py (3 functions) - edisgo/io/heat_pump_import.py (2 functions) - edisgo/io/generators_import.py (1 function) - edisgo/io/storage_import.py (1 function) - edisgo/network/heat.py (2 engine fallbacks) - edisgo/network/timeseries.py (1 engine fallback) ## Local DB schema resolution (edisgo/tools/config.py) Extend `import_tables_from_oep()` to handle local egon-data databases: - Auto-resolve schema mismatches: tables may reside in different schemas locally vs on OEP (e.g. egon_etrago_bus is in "grid" locally but imported via "supply" on OEP). The method now searches all schemas when a table is not found in the expected schema. - Handle tables without primary keys (e.g. egon_map_zensus_grid_districts, egon_daily_heat_demand_per_climate_zone) by passing all columns as synthetic PK via `__mapper_args__["primary_key"]`. ## SSH tunnel lifecycle management (edisgo/io/db.py) - `ssh_tunnel()` now returns `tuple[str, SSHTunnelForwarder]` instead of just the port string, so the server object is no longer lost - `engine()` stores the SSH server as `engine._ssh_server` for later cleanup via `server.stop()` - This fixes the "I/O operation on closed file" logging errors caused by orphaned paramiko keepalive threads writing to closed log handlers ## Test infrastructure (tests/conftest.py) - Add `--runlocal` flag to run all DB tests against both OEP and local egon-data database - Add `--egon-data-config` flag for custom YAML config path - Parametrize tests via `db_engine` fixture: each DB test runs as `test_name[oep]` and `test_name[local]` when --runlocal is active - Add `pytest_sessionfinish` hook that disposes engines and stops SSH tunnels cleanly after all tests complete - Suppress paramiko DEBUG keepalive logging (defense-in-depth) - Migrate all 29 test methods across 10 test files from hardcoded `pytest.engine` to parametrized `db_engine` fixture ## New files - tests/io/test_db.py: 4 tests for SSH tunnel lifecycle (tunnel returns server, engine stores server, cleanup stops tunnel, OEP has no tunnel) - egon-data.configuration.yaml: Template config file with placeholder credentials for local egon-data database connections
6c1ba59 to
ffd43c2
Compare
This was referenced Mar 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
(OEP + local egon-data)
This commit introduces a comprehensive refactoring of how eDisGo manages database connections, enabling seamless switching between the OpenEnergyPlatform (OEP) and local egon-data PostgreSQL databases.
Engine as EDisGo property (edisgo/edisgo.py)
engineproperty to the EDisGo class, replacing the previous pattern of passing engine explicitly to every function callengine,db_config_path,db_url,db_ssh,db_token__deepcopy__method that excludes the unpicklable SQLAlchemy engine (contains _thread._local objects) and lets the copy lazily recreate its own connectionOptional engine parameter in all DB functions
Make the
engineparameter optional (default=None) in all 33 functions across 8 modules that access the database. When engine is None, each function falls back toedisgo_object.engine. This maintains full backward compatibility — callers can still pass an explicit engine.Modified modules:
Local DB schema resolution (edisgo/tools/config.py)
Extend
import_tables_from_oep()to handle local egon-data databases:__mapper_args__["primary_key"].SSH tunnel lifecycle management (edisgo/io/db.py)
ssh_tunnel()now returnstuple[str, SSHTunnelForwarder]instead of just the port string, so the server object is no longer lostengine()stores the SSH server asengine._ssh_serverfor later cleanup viaserver.stop()Test infrastructure (tests/conftest.py)
--runlocalflag to run all DB tests against both OEP and local egon-data database--egon-data-configflag for custom YAML config pathdb_enginefixture: each DB test runs astest_name[oep]andtest_name[local]when --runlocal is activepytest_sessionfinishhook that disposes engines and stops SSH tunnels cleanly after all tests completepytest.engineto parametrizeddb_enginefixtureNew files
Fixes #450 #578
Type of change
Please delete options that are not relevant.
Checklist:
pre-commithooks