Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.5] - 2026-05-07

### Fixed

- `skypro simulate` now skips constructing a SQLAlchemy engine for the
flows database when all imbalance data sources are `csvTimeseries`.
Previously the engine was created eagerly and unconditionally, so a
placeholder or unparseable `env_config["flows"]["dbUrl"]` raised
before any simulation logic ran — blocking standalone CSV-only
configuration bundles. When at least one source is `flowsMarketData`
the engine is still constructed, with a clearer error if `dbUrl` is
missing.

## [2.0.0] - Unreleased

### Breaking Changes
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "skypro"
version = "2.0.4"
version = "2.0.5"
description = "Skyprospector by Cepro"
authors = ["damonrand <damon@cepro.energy>"]
license = "MIT"
Expand Down
29 changes: 28 additions & 1 deletion src/skypro/commands/simulator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,33 @@ def _get_rates_from_config(
and a dataframe containing live and final imbalance data.
"""

# Build the flows DB engine only if at least one imbalance source is
# `flowsMarketData`. CSV-only configs (`csvTimeseries` for all four
# sources) don't need the engine — `get_timeseries` accepts
# `db_engine=None` and only forwards it down the flowsMarketData
# branch. Mirrors the rates-DB gating pattern used below for
# `rates_config.{live,final}.rates_db`.
imbalance_sources = [
rates_config.final.imbalance_data_source.price,
rates_config.final.imbalance_data_source.volume,
rates_config.live.imbalance_data_source.price,
rates_config.live.imbalance_data_source.volume,
]
needs_flows_db = any(
s.flows_market_data_source is not None for s in imbalance_sources
)
if needs_flows_db:
try:
flows_db_url = env_config["flows"]["dbUrl"]
except KeyError:
raise SystemExit(
"simulate.yaml references a flowsMarketData imbalance source, but "
"env_config['flows']['dbUrl'] is not set in the environment file."
)
db_engine = sqlalchemy.create_engine(flows_db_url)
else:
db_engine = None

def read_imbalance_data(source: TimeseriesDataSource, context: str):
"""
Convenience function for reading imbalance data
Expand All @@ -519,7 +546,7 @@ def read_imbalance_data(source: TimeseriesDataSource, context: str):
start=time_index[0],
end=time_index[-1],
file_path_resolver_func=file_path_resolver_func,
db_engine=sqlalchemy.create_engine(env_config["flows"]["dbUrl"]),
db_engine=db_engine,
context=context
)
for notice in notices:
Expand Down
15 changes: 15 additions & 0 deletions src/tests/integration/fixtures/env_no_flows_db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"_doc": "Variant of env.json with an unparseable flows.dbUrl. Used to verify that skypro simulate does NOT eagerly construct a SQLAlchemy engine for the flows DB when all imbalance sources are csvTimeseries (the case for standalone CSV-only handoff bundles). See test_integration_simulator.py::test_integration_csv_only_no_flows_db.",
"vars": {
},
"flows": {
"dbUrl": "REPLACE_OR_REMOVE_NOT_USED_BY_SIMULATE"
},
"flux": {
"dbUrl": "postgresql://username:password@someserver.com:5687/database",
"schema": "flows"
},
"rates": {
"dbUrl": "postgresql://username:password@someserver.com:5687/database"
}
}
46 changes: 46 additions & 0 deletions src/tests/integration/test_integration_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,49 @@ class SubTest:
0,
msg=f"Summary value out of tolerance:\n{error.transpose()}"
)

def test_integration_csv_only_no_flows_db(self):
"""
Regression test for the eager-flows-DB bug fixed in v2.0.5.

The integration fixture has only csvTimeseries imbalance sources
and file-based rates — no flowsMarketData, no rates_db. This
means env_config["flows"]["dbUrl"] is never queried at runtime.
Before v2.0.5, the simulator constructed a SQLAlchemy engine for
the flows DB unconditionally, so an unparseable placeholder URL
in flows.dbUrl would crash the run before any simulation logic
executed — blocking standalone CSV-only handoff bundles.

env_no_flows_db.json sets flows.dbUrl to the literal placeholder
string "REPLACE_OR_REMOVE_NOT_USED_BY_SIMULATE" — SQLAlchemy
can't parse it as a URL. With the v2.0.5 fix, the simulator
skips engine construction when no source is flowsMarketData and
the run completes successfully.
"""
print(
"\n\n\n\nSTARTING SIMULATION INTEGRATION TEST 'csv_only_no_flows_db' "
"- - - - - - - - - - - - - - - - - - - - -"
)

res = subprocess.run([
'python3',
'./src/skypro/main.py',
'simulate',
'--env',
'./src/tests/integration/fixtures/env_no_flows_db.json',
'-y',
'--config',
'./src/tests/integration/fixtures/simulation/config.yaml',
'--sim',
'integrationTestPriceCurve',
])

self.assertEqual(
res.returncode,
0,
msg=(
"Simulator exited non-zero with an unparseable flows.dbUrl "
"in the env file, despite all imbalance sources being "
"csvTimeseries. Pre-v2.0.5 regression."
),
)
Loading