From cf9871cb46076ef6592ef579217bf6dde1dd62dc Mon Sep 17 00:00:00 2001 From: bartzbeielstein <32470350+bartzbeielstein@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:32:44 +0200 Subject: [PATCH 1/4] chore(deps): lock spotforecast2-safe 22.1.0 (build_zone_qc_frame consumer contract) Co-Authored-By: Claude Fable 5 --- uv.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uv.lock b/uv.lock index ddac7dee..6f4d8a47 100644 --- a/uv.lock +++ b/uv.lock @@ -3491,7 +3491,7 @@ wheels = [ [[package]] name = "spotforecast2" -version = "8.0.0rc1" +version = "8.0.0" source = { editable = "." } dependencies = [ { name = "astral" }, @@ -3590,7 +3590,7 @@ dev = [ [[package]] name = "spotforecast2-safe" -version = "22.0.0" +version = "22.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astral" }, @@ -3607,9 +3607,9 @@ dependencies = [ { name = "statsmodels" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/89/00f24736a21666be93e6e59fdc0958e497178eb890ab5cefadd88e6d980f/spotforecast2_safe-22.0.0.tar.gz", hash = "sha256:f2c411e074b41a0421185c58a086b6e61b90d4d4fcab9860a93fa7faa09e18bf", size = 20630930, upload-time = "2026-06-10T23:51:54.156Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/7b/9f7dcc944bde99ae1fbf4c329b95598bed4bb2c1c5ca04b879574bd42027/spotforecast2_safe-22.1.0.tar.gz", hash = "sha256:32666cc9bf320c18d146a24a1e803abfc1de821cf63da2f226e009808f52187c", size = 20654907, upload-time = "2026-06-12T07:21:36.071Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/5a/d2bd77a6233ea08df61903391ab27bfe810dc4e107c2f452500e04c73f97/spotforecast2_safe-22.0.0-py3-none-any.whl", hash = "sha256:18906e1371cd93f03af4dbead15bb94db7945624aa24777cf12c06fe2d951e23", size = 20696710, upload-time = "2026-06-10T23:51:51.911Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a3/a12c5d4fa72350cf6741f53d1d3123385a50117b0592fd7bc7f59038018c/spotforecast2_safe-22.1.0-py3-none-any.whl", hash = "sha256:e8f66ea353c56b64752ae5d44f01e0143ed725c1724108b54e87a25b9e9456df", size = 20723105, upload-time = "2026-06-12T07:21:31.953Z" }, ] [[package]] From 5f5d0965de936b11e03738f9669ab0ed4b867c01 Mon Sep 17 00:00:00 2001 From: bartzbeielstein <32470350+bartzbeielstein@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:53:18 +0200 Subject: [PATCH 2/4] refactor(model_selection): extract _position_flag, drop shadowing import (CodeQL) Resolves both open code-scanning alerts: - Alert 85 (py/redundant-comparison, warning): CodeQL mis-normalizes the inline "pos > 1 - warn_frac" / "pos < warn_frac" ternary in boundary_report and flags the second test as always true. Hoist the threshold into a named variable inside a shared _position_flag() helper, which also deduplicates the identical flag logic in report_boundary_positions and boundary_report. - Alert 84 (py/repeated-import, note): remove the function-level "import logging" in tests/test_multitask.py that shadowed the module-level import. No behavior change; black-format the module. Co-Authored-By: Claude Fable 5 --- src/spotforecast2/model_selection/boundary.py | 42 ++++++++++++------- tests/test_multitask.py | 2 - 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/spotforecast2/model_selection/boundary.py b/src/spotforecast2/model_selection/boundary.py index bd40373e..58e3f1e0 100644 --- a/src/spotforecast2/model_selection/boundary.py +++ b/src/spotforecast2/model_selection/boundary.py @@ -63,6 +63,26 @@ _logger = logging.getLogger(__name__) +def _position_flag(pos: float, warn_frac: float) -> str: + """Classify a normalized boundary position as near-upper, near-lower, or interior. + + Args: + pos: Position of the tuned value inside its dimension, normalized to + ``[0, 1]`` in the dimension's own scale. + warn_frac: Fraction of the range defining the "near-boundary" zone at + each end. + + Returns: + ``"> upper"``, ``"< lower"``, or ``""`` (interior). + """ + upper_zone = 1.0 - warn_frac + if pos > upper_zone: + return "> upper" + if pos < warn_frac: + return "< lower" + return "" + + def report_boundary_positions( params: Mapping[str, float | int], search_space: Mapping[str, Any], @@ -158,11 +178,7 @@ def report_boundary_positions( ) else: pos = (float(val) - low) / (high - low) - flag = ( - "> upper" - if pos > 1 - warn_frac - else ("< lower" if pos < warn_frac else "") - ) + flag = _position_flag(pos, warn_frac) log.info( " bound %-18s = %-11.5g in [%g, %g]%s pos=%.2f%s", key, @@ -266,11 +282,7 @@ def boundary_report( ) else: pos = (float(val) - low) / (high - low) - flag = ( - "> upper" - if pos > 1 - warn_frac - else ("< lower" if pos < warn_frac else "") - ) + flag = _position_flag(pos, warn_frac) rows.append( { "param": name.replace("estimator__", ""), @@ -298,7 +310,9 @@ def boundary_report( "Use report_boundary_positions() instead if you have unprefixed keys.", numeric_dims, ) - return pd.DataFrame(columns=["param", "low", "high", "value", "scale", "position", "flag"]) + return pd.DataFrame( + columns=["param", "low", "high", "value", "scale", "position", "flag"] + ) return df.sort_values("position", ascending=False).reset_index(drop=True) @@ -389,11 +403,7 @@ def suggest_bounds( """ report = boundary_report(best_params, search_space, warn_frac=warn_frac) - flagged = { - row["param"]: row["flag"] - for _, row in report.iterrows() - if row["flag"] - } + flagged = {row["param"]: row["flag"] for _, row in report.iterrows() if row["flag"]} out: dict[str, Any] = {} for name, spec in search_space.items(): if not (isinstance(spec, tuple) and len(spec) in (2, 3)): diff --git a/tests/test_multitask.py b/tests/test_multitask.py index 1fa26a79..235ff13b 100644 --- a/tests/test_multitask.py +++ b/tests/test_multitask.py @@ -814,8 +814,6 @@ class TestCacheHomeResolution: """BaseTask must resolve cache_home=None to get_cache_home().""" def test_cache_home_none_resolves_to_default(self, tmp_path, monkeypatch): - import logging - from spotforecast2_safe.data.fetch_data import get_cache_home # Redirect the package default so the test never touches the From d54dded014300f3d6704e8b68040e2dbaf90826e Mon Sep 17 00:00:00 2001 From: bartzbeielstein <32470350+bartzbeielstein@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:53:27 +0200 Subject: [PATCH 3/4] chore(deps): lock spotforecast2-safe 22.2.0 (event-window consumer contract) The lecture qmd now passes include_football_match_window to ConfigEntsoe (sf2-safe 22.2.0 event windows), which the team4 consumer-contract gate correctly flagged against the 22.1.0 lock. Bump the lock (pin >=22,<23 already allows it) and sync the lockfile self-version to 8.1.0. Add the two new provider flags (include_football_match_window, include_energy_saving_window) to the exog-provider re-export test and assert registry membership as a superset so additive provider releases in sf2-safe no longer break sf2's suite; removals still fail. Co-Authored-By: Claude Fable 5 --- tests/test_exog_providers_reexport.py | 6 +++++- uv.lock | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/test_exog_providers_reexport.py b/tests/test_exog_providers_reexport.py index 30a74322..38481c96 100644 --- a/tests/test_exog_providers_reexport.py +++ b/tests/test_exog_providers_reexport.py @@ -30,6 +30,8 @@ "include_entsoe_renewable_forecast", "include_entsoe_net_load", "include_entsoe_day_ahead_price", + "include_football_match_window", + "include_energy_saving_window", } @@ -46,7 +48,9 @@ def test_registry_reexported_from_preprocessing(): build_providers_from_config, ) - assert set(EXOG_PROVIDER_REGISTRY) == EXPECTED_FLAGS + # Superset, not equality: additive provider releases in sf2-safe must not + # break sf2's suite; removals of expected flags still fail. + assert EXPECTED_FLAGS <= set(EXOG_PROVIDER_REGISTRY) assert issubclass(CovidInfectionRateProvider, ExogFeatureProvider) assert callable(build_providers) and callable(build_providers_from_config) # silence unused-import linters for the re-export assertions diff --git a/uv.lock b/uv.lock index 6f4d8a47..13d02a26 100644 --- a/uv.lock +++ b/uv.lock @@ -3491,7 +3491,7 @@ wheels = [ [[package]] name = "spotforecast2" -version = "8.0.0" +version = "8.1.0" source = { editable = "." } dependencies = [ { name = "astral" }, @@ -3590,7 +3590,7 @@ dev = [ [[package]] name = "spotforecast2-safe" -version = "22.1.0" +version = "22.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astral" }, @@ -3607,9 +3607,9 @@ dependencies = [ { name = "statsmodels" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/7b/9f7dcc944bde99ae1fbf4c329b95598bed4bb2c1c5ca04b879574bd42027/spotforecast2_safe-22.1.0.tar.gz", hash = "sha256:32666cc9bf320c18d146a24a1e803abfc1de821cf63da2f226e009808f52187c", size = 20654907, upload-time = "2026-06-12T07:21:36.071Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/fd/685a4d9797d467ec646c3ffc75b8d6327ff770a7540b47c5a0300f23aac5/spotforecast2_safe-22.2.0.tar.gz", hash = "sha256:fd458aea0a6421cc8229cdbc2314b9a0863508771c286ac2537c8d3221eb1362", size = 20660329, upload-time = "2026-06-12T11:53:21.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/a3/a12c5d4fa72350cf6741f53d1d3123385a50117b0592fd7bc7f59038018c/spotforecast2_safe-22.1.0-py3-none-any.whl", hash = "sha256:e8f66ea353c56b64752ae5d44f01e0143ed725c1724108b54e87a25b9e9456df", size = 20723105, upload-time = "2026-06-12T07:21:31.953Z" }, + { url = "https://files.pythonhosted.org/packages/30/1b/24262db44be056e62680e4ef28e8423bd977abeac68b42c15d045f6941e3/spotforecast2_safe-22.2.0-py3-none-any.whl", hash = "sha256:1b79b7d132024103da23ccfb4e8057583e3d04906574e633c78f97e163301da9", size = 20729694, upload-time = "2026-06-12T11:53:19.078Z" }, ] [[package]] From 3e3e67339d69ca5b8ea84d4f0fd3cb8b1ecca0e9 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 12 Jun 2026 20:30:50 +0000 Subject: [PATCH 4/4] chore(release): 8.1.1-rc.1 [skip ci] ## [8.1.1-rc.1](https://github.com/sequential-parameter-optimization/spotforecast2/compare/v8.1.0...v8.1.1-rc.1) (2026-06-12) ### Code Refactoring * **model_selection:** extract _position_flag, drop shadowing import (CodeQL) ([5f5d096](https://github.com/sequential-parameter-optimization/spotforecast2/commit/5f5d0965de936b11e03738f9669ab0ed4b867c01)) --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 429249eb..abfffe06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [8.1.1-rc.1](https://github.com/sequential-parameter-optimization/spotforecast2/compare/v8.1.0...v8.1.1-rc.1) (2026-06-12) + +### Code Refactoring + +* **model_selection:** extract _position_flag, drop shadowing import (CodeQL) ([5f5d096](https://github.com/sequential-parameter-optimization/spotforecast2/commit/5f5d0965de936b11e03738f9669ab0ed4b867c01)) + ## [8.1.0](https://github.com/sequential-parameter-optimization/spotforecast2/compare/v8.0.0...v8.1.0) (2026-06-12) ### Features diff --git a/pyproject.toml b/pyproject.toml index 9e8e3ada..5e7d33d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "spotforecast2" -version = "8.1.0" +version = "8.1.1-rc.1" description = "Forecasting with spot" readme = "README.md" license = { text = "AGPL-3.0-or-later" }