Skip to content

Conversation

@ParticularlyPythonicBS
Copy link
Member

@ParticularlyPythonicBS ParticularlyPythonicBS commented Dec 7, 2025

Standardizes the logging and timing mechanisms using a reusable context manager to handle operation timing.

New sample output

[17:57:51] INFO     Full run mode selected. Calling start().
           INFO     Executing scenario in mode: TemoaMode.PERFECT_FORESIGHT
           INFO     Loading data dictionary
           INFO     Starting network analysis for region utopia
           INFO     Generated graph HTML at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-08_175750/Network_Graph_utopia_1990.html
           INFO     Generated network graphs at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-08_175750/Network_Graph_utopia_1990.html
           INFO     Generated graph HTML at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-08_175750/Network_Graph_utopia_2000.html
           INFO     Generated network graphs at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-08_175750/Network_Graph_utopia_2000.html
           INFO     Generated graph HTML at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-08_175750/Network_Graph_utopia_2010.html
           INFO     Generated network graphs at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-08_175750/Network_Graph_utopia_2010.html
[17:57:52] INFO     Started: Creating model instance
           INFO     Running a seasonal time slice database. This behaviour can be changed using the time_sequencing parameter in the config file.
           INFO     Missing some time slices for Demand Specific Distribution ('utopia', 1990, 'RH'): {('summer', 'day'), ('summer', 'night')}
           INFO     Missing some time slices for Demand Specific Distribution ('utopia', 2010, 'RH'): {('summer', 'day'), ('summer', 'night')}
           INFO     Missing some time slices for Demand Specific Distribution ('utopia', 2000, 'RH'): {('summer', 'day'), ('summer', 'night')}
           INFO     Finished: Creating model instance (Time taken: 0.05s)
           INFO     Model built... Variables: 1095, Constraints: 1486
           INFO     Started price checking model: unknown
           INFO     Finished Price Checking Build Action
           INFO     Started: Solving model unknown
           INFO     Finished: Solving model unknown (Time taken: 0.12s)
           INFO     Solver termination condition: optimal (optimal: True)
           INFO     Started: Processing results
           INFO     Finished: Processing results (Time taken: 0.11s)
           INFO     Total Cost value: 34711.52

Current output:

[11:04:48] INFO     Full run mode selected. Calling start().                                                                                                                                                     
           INFO     Executing scenario in mode: TemoaMode.PERFECT_FORESIGHT                                                                                                                                      
           INFO     Loading data dictionary                                                                                                                                                                      
           INFO     Starting network analysis for region utopia                                                                                                                                                  
           INFO     Generated graph HTML at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-07_110447/Network_Graph_utopia_1990.html                                                          
           INFO     Generated network graphs at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-07_110447/Network_Graph_utopia_1990.html                                                      
           INFO     Generated graph HTML at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-07_110447/Network_Graph_utopia_2000.html                                                          
           INFO     Generated network graphs at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-07_110447/Network_Graph_utopia_2000.html                                                      
           INFO     Generated graph HTML at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-07_110447/Network_Graph_utopia_2010.html                                                          
           INFO     Generated network graphs at: /media/Secondary/Projects/TemoaProject/temoa/output_files/2025-12-07_110447/Network_Graph_utopia_2010.html                                                      
[        ] Creating model instance.           INFO     Started creating model instance from data                                                                                                                                                    
           INFO     Running a seasonal time slice database. This behaviour can be changed using the time_sequencing parameter in the config file.                                                                
           INFO     Missing some time slices for Demand Specific Distribution ('utopia', 2000, 'RH'): {('summer', 'day'), ('summer', 'night')}                                                                   
           INFO     Missing some time slices for Demand Specific Distribution ('utopia', 1990, 'RH'): {('summer', 'day'), ('summer', 'night')}                                                                   
           INFO     Missing some time slices for Demand Specific Distribution ('utopia', 2010, 'RH'): {('summer', 'day'), ('summer', 'night')}                                                                   
[    0.05] Instance created.       
[11:04:49] INFO     Finished creating model instance from data                                                                                                                                                   
           INFO     model built...  Variables: 1095, Constraints: 1486                                                                                                                                           
           INFO     Started price checking model: unknown                                                                                                                                                        
           INFO     Finished Price Checking Build Action                                                                                                                                                         
[        ] Solving.           INFO     Starting the solve process using appsi_highs solver on model unknown                                                                                                                         
           INFO     Solve process complete                                                                                                                                                                       
[    0.13] Model solved.
           INFO     Solver termination condition: optimal (optimal: True)                                                                                                                                        
[    0.11] Results processed.                                    .
           INFO     total_cost value: 34711.52    

Summary by CodeRabbit

  • New Features

    • Added a public, silenceable timing utility for consistent start/finish phase messages.
    • Broadened solver handling with validated, sanitized solver-suffix support and consolidated solver options.
    • Relaxed public API to accept more flexible solver-suffix inputs.
  • Bug Fixes

    • Safer solver error handling with clearer termination/status logging and conditional result storage.
    • LP output directories are auto-created.
    • Graphviz generation fails gracefully and cleans up on error.
  • Quality

    • Console logs now include timestamps and more consistent model/metric reporting; suffix warnings.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 7, 2025

Walkthrough

Adds a public task_timer context manager and applies it across build/solve/result workflows; refactors solver option/suffix handling and termination extraction; improves DB metadata/version checks and connection lifecycles; ensures LP and Graphviz I/O robustness; enables console log timestamps.

Changes

Cohort / File(s) Summary
Core runtime & solver workflow
temoa/_internal/run_actions.py
Adds public task_timer(action_name: str, *, silent: bool = False) and replaces ad-hoc timing in build, solve, and result handling; consolidates solver option handling (branches for cplex, gurobi, appsi_highs, cbc preserved); validates/sanitizes solver_suffixes against {'dual','slack','rc'} and passes sanitized list to solvers; wraps solver execution in task_timer; enhances solver result/termination extraction for legacy and APPSI formats; conditionally stores suffix-derived outputs only on optimal termination; standardizes logging and model counting; ensures LP directories are created and LP paths written as strings.
Database metadata & I/O robustness
temoa/_internal/run_actions.py
Switches metadata table name to metadata for version checks, iterates both input/output DBs, ensures connections are closed, unifies error messages to reference DB names/versions; improves Graphviz plotting error handling and coerces path objects to strings for filesystem ops.
CLI logging
temoa/cli.py
Enables console timestamps by setting show_time=True on RichHandler in _setup_logging.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect solver-branch logic for cplex, gurobi, and appsi_highs to verify options passed and side effects.
  • Verify solver_suffixes validation/warnings and that sanitized suffixes are propagated and stored only on optimal termination.
  • Audit termination/status extraction paths for legacy vs APPSI result formats and fallbacks.
  • Confirm DB metadata/version queries, connection open/close, and error messages are correct and resource-safe.
  • Check LP saving (directory creation and string casting) and Graphviz error handling/cleanup.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: refactoring logging and timing infrastructure (task_timer context manager) and improving CLI output consistency (aligned INFO messages, timestamps, elapsed time display).
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ref/task_timer_context_manager

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4eefc48 and e637077.

📒 Files selected for processing (2)
  • temoa/_internal/run_actions.py (11 hunks)
  • temoa/cli.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during code reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in the database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
🪛 Ruff (0.14.8)
temoa/_internal/run_actions.py

174-174: Boolean-typed positional argument in function definition

(FBT001)


174-174: Boolean default positional argument in function definition

(FBT002)


251-251: Redundant exception object included in logging.exception call

(TRY401)


252-255: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


259-259: Do not catch blind exception: Exception

(BLE001)


261-264: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


265-265: Avoid specifying long messages outside the exception class

(TRY003)


387-387: Logging .exception(...) should be used instead of .error(..., exc_info=True)

(G201)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: setup and test (windows-latest, 3.12)
  • GitHub Check: setup and test (windows-latest, 3.13)
  • GitHub Check: setup and test (macos-latest, 3.12)
  • GitHub Check: setup and test (macos-latest, 3.13)
🔇 Additional comments (9)
temoa/cli.py (1)

53-53: LGTM: Timestamp display enabled for console logs.

Enabling show_time=True with the [%X] format (line 54) produces the timestamp display requested in the PR objectives. This aligns with the requirement to show per-step timestamps in square brackets to the left of INFO messages.

temoa/_internal/run_actions.py (8)

33-50: LGTM: Well-implemented task timer context manager.

The task_timer context manager correctly uses perf_counter() for accurate timing, ensures the finish message is logged via the finally block even when exceptions occur, and properly implements silent as keyword-only. This provides the consistent "Started"/"Finished (Time taken: Xs)" logging pattern described in the PR objectives.


143-154: LGTM: Clean timing instrumentation and efficient counting.

Wrapping instance creation in task_timer provides the consistent timing logs described in the PR objectives. The switch to sum() with generator expressions (lines 151-152) is more concise and efficient than explicit iteration.


228-239: LGTM: Robust suffix validation.

The suffix validation correctly filters the provided suffixes against the legitimate set {'dual', 'slack', 'rc'}, warns about invalid entries, and sanitizes the list before passing it to the solver. This prevents errors from unsupported suffix names.


243-243: LGTM: Consistent timing for solve operation.

Wrapping the solver call in task_timer provides the "Started"/"Finished" messages with elapsed times as described in the PR objectives, giving clear visibility into solve duration.


339-347: LGTM: Consistent timing and simplified conditional logic.

Wrapping result processing in task_timer (line 339) aligns with the PR objectives. The simplified conditional results if config.save_duals else None (line 343) eliminates code duplication while maintaining the same behavior based on TableWriter.write_results accepting None for results_with_duals.


378-390: LGTM: Robust Graphviz error handling.

The nested try/finally structure ensures graph_gen.close() is called (line 389) even if an exception occurs during diagram generation, preventing resource leaks. The error logging at line 387 appropriately captures the exception details.


204-226: The CPLEX and Gurobi solver parameters are appropriately sourced from PyPSA documentation and are well-suited for Temoa's energy system optimization use case. The barrier method with no crossover and convergence tolerance of 1.0e-5 is proven best practice for large-scale linear programming problems in energy modeling and will not inadvertently change solution behavior—these are deterministic solver configurations used consistently across production energy system models.


84-87: The metadata table is correctly defined as lowercase metadata in the v4 schema (temoa/db_schema/temoa_schema_v4.sql) and the queries at lines 84 and 87 match this definition. No case mismatch exists and the queries will execute correctly.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
temoa/_internal/run_actions.py (1)

178-277: Harden error-path logging for solver status extraction

The solver configuration branches (cbc/cplex/gurobi/appsi_highs), suffix validation, and task_timer wrapper all look reasonable. However, the current error handling path has a robustness gap:

if result:
    logger.error(
        'Solver reported termination condition (if any): %s',
        result['Solution'].Status,
    )

This direct access to result['Solution'].Status will raise KeyError/TypeError/AttributeError if the result object lacks a 'Solution' entry or is not dictionary-like (e.g., APPSI results or partially populated legacy results). This masks the original solver failure.

Since check_solve_status already exists and safely handles both legacy and APPSI results, use it defensively here:

if result:
    try:
        _ok, status_msg = check_solve_status(result)
    except Exception:
        status_msg = '<unable to extract status>'
    logger.error(
        'Solver reported termination/status (if any): %s',
        status_msg,
    )

Also, the docstring mentions 'duals' (plural) as a supported suffix, but legit_suffixes uses 'dual' (singular). Align the documentation with the actual allowed suffix names to prevent user confusion.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8dda04a and c01cc6a.

📒 Files selected for processing (2)
  • temoa/_internal/run_actions.py (10 hunks)
  • temoa/cli.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
temoa/_internal/run_actions.py (3)
temoa/extensions/monte_carlo/mc_run.py (1)
  • model (185-192)
temoa/_internal/table_writer.py (1)
  • write_results (91-135)
temoa/data_processing/db_to_excel.py (1)
  • make_excel (25-241)
🪛 Ruff (0.14.7)
temoa/_internal/run_actions.py

34-34: Boolean-typed positional argument in function definition

(FBT001)


34-34: Boolean default positional argument in function definition

(FBT002)


96-100: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


181-181: Boolean-typed positional argument in function definition

(FBT001)


181-181: Boolean default positional argument in function definition

(FBT002)


268-268: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: setup and test (macos-latest, 3.12)
  • GitHub Check: setup and test (macos-latest, 3.13)
  • GitHub Check: setup and test (windows-latest, 3.12)
  • GitHub Check: setup and test (windows-latest, 3.13)
🔇 Additional comments (5)
temoa/cli.py (1)

48-55: RichHandler timestamp enabling aligns with new timing logs

Setting show_time=True with log_time_format='[%X]' is consistent with the new timer-based INFO logs and respects the existing debug/silent level handling; I don’t see regressions here.

temoa/_internal/run_actions.py (4)

127-161: Model-building timer and stats changes look good

Wrapping model.create_instance in task_timer('Creating model instance', ...) and computing constraint/variable counts via sum(len(...)) over component_objects is a sensible refactor and should behave well for typical Pyomo models. The final info log on variable/constraint counts is clear.


280-333: check_solve_status helper is a nice, solver-agnostic improvement

Using check_optimal_termination(result) plus the layered extraction of termination_condition and status (legacy result['Solution'].Status, then APPSI-style attributes, then a string fallback) makes status reporting much more robust across different solver plugins.

The final return of (False, f'{status_msg} was returned from solve') for non‑optimal cases is clear and should integrate well with callers. I don’t see issues here; just ensure you exercise this with both classic Pyomo solvers and appsi_highs in tests to validate the logging output.


335-371: Result processing timer and Excel naming changes look consistent

  • Wrapping result processing in task_timer('Processing results', silent=config.silent) is straightforward and keeps logging consistent with the rest of the pipeline.
  • Passing results_with_duals=results only when config.save_duals matches the intended TableWriter.write_results contract.
  • The Excel branch’s scenario_name = f'{config.scenario}-{iteration}' if iteration is not None else config.scenario combined with temp_scenario = {scenario_name} and excel_filename = config.output_path / scenario_name should give distinct Excel files per iteration, assuming the DB uses the same scenario naming convention.

The updated total-cost log ('Total Cost value: %0.2f') is fine and keeps the message explicit. Please verify one multi‑iteration run end‑to‑end (including Excel and Graphviz) to confirm scenario naming is aligned across DB, Excel, and plots.


164-176: Current implementation is correct for actual usage patterns; the concern about file path handling is unfounded.

All verified call sites pass directory paths to save_lp():

  • sv_mga_sequencer.py: self.config.output_path / 'base_model'
  • sv_mga_sequencer.py: self.config.output_path / 'option_model'
  • temoa_sequencer.py: self.config.output_path
  • myopic_sequencer.py: self.config.output_path / 'LP...'

No existing callers pass full file paths (e.g., /path/to/model.lp), so the regression scenario described in the review comment does not occur. The current implementation correctly treats lp_path as a directory root and appends model.lp, which aligns with all actual usage in the codebase. The suggested diff to support both patterns is unnecessary.

Likely an incorrect or invalid review comment.

@ParticularlyPythonicBS ParticularlyPythonicBS force-pushed the ref/task_timer_context_manager branch from c01cc6a to 54bb577 Compare December 7, 2025 16:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c01cc6a and 54bb577.

📒 Files selected for processing (2)
  • temoa/_internal/run_actions.py (9 hunks)
  • temoa/cli.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during code reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in the database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
🧬 Code graph analysis (1)
temoa/_internal/run_actions.py (2)
temoa/_internal/table_writer.py (1)
  • write_results (91-135)
temoa/data_processing/db_to_excel.py (1)
  • make_excel (25-241)
🪛 Ruff (0.14.7)
temoa/_internal/run_actions.py

179-179: Boolean-typed positional argument in function definition

(FBT001)


179-179: Boolean default positional argument in function definition

(FBT002)


256-256: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


257-260: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


264-264: Do not catch blind exception: Exception

(BLE001)


266-269: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


270-270: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: setup and test (windows-latest, 3.12)
  • GitHub Check: setup and test (windows-latest, 3.13)
  • GitHub Check: setup and test (macos-latest, 3.13)
  • GitHub Check: setup and test (macos-latest, 3.12)
🔇 Additional comments (7)
temoa/cli.py (1)

49-55: LGTM!

Enabling show_time=True correctly activates the timestamp display that was already configured via log_time_format. This aligns well with the new task_timer context manager to provide consistent timing information in CLI output.

temoa/_internal/run_actions.py (6)

33-50: LGTM!

The task_timer context manager is well-implemented with:

  • Correct use of perf_counter for accurate timing
  • Keyword-only silent parameter (as suggested in past review)
  • finally block ensures elapsed time is logged even when exceptions occur

87-107: Database version check improvements look good.

The defensive int() casting (lines 93-94) and explicit handling of both input and output databases are solid improvements. The finally block ensures connections are properly closed.


147-159: LGTM!

Clean integration of task_timer for instance creation timing, and the sum() with generator expressions is memory-efficient for counting model components.


282-334: LGTM!

The solver-agnostic status extraction with cascading fallbacks for legacy and APPSI formats is well-designed. The defensive approach handles various solver result object structures gracefully.


374-403: LGTM!

Good defensive error handling for Graphviz generation. Since this is supplementary output, catching exceptions and logging with exc_info=True prevents optional visualization failures from crashing the main workflow.


209-230: Solver configuration values are current and match PyPSA defaults.

The parameter values for both cplex and gurobi are verified against PyPSA documentation:

  • CPLEX: lpmethod=4 (barrier), solutiontype=2 (non-basic, disables crossover), barrier convergetol=1.0e-5, feasopt tolerance=1.0e-6 — all align with PyPSA's cplex-default configuration.
  • Gurobi: Method=2 (barrier), Crossover=0 (no crossover), BarConvTol=1.0e-5, FeasibilityTol=1.0e-6 — all match documented PyPSA patterns and solver API expectations.

The explicit pass statements for cbc and appsi_highs appropriately indicate no additional configuration is needed for these solvers.

@ParticularlyPythonicBS ParticularlyPythonicBS force-pushed the ref/task_timer_context_manager branch from 54bb577 to 323da03 Compare December 7, 2025 18:04
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54bb577 and 323da03.

📒 Files selected for processing (2)
  • temoa/_internal/run_actions.py (9 hunks)
  • temoa/cli.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during code reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in the database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
🪛 Ruff (0.14.7)
temoa/_internal/run_actions.py

178-178: Boolean-typed positional argument in function definition

(FBT001)


178-178: Boolean default positional argument in function definition

(FBT002)


255-255: Redundant exception object included in logging.exception call

(TRY401)


256-259: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


263-263: Do not catch blind exception: Exception

(BLE001)


265-268: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


269-269: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: setup and test (windows-latest, 3.13)
  • GitHub Check: setup and test (macos-latest, 3.13)
  • GitHub Check: setup and test (windows-latest, 3.12)
  • GitHub Check: setup and test (macos-latest, 3.12)
🔇 Additional comments (10)
temoa/cli.py (1)

49-55: LGTM!

Enabling show_time=True aligns console output with the new task_timer logging approach, providing consistent timestamps for all logged operations. The [%X] format displays time in locale's appropriate representation.

temoa/_internal/run_actions.py (9)

5-11: LGTM!

The new imports support the task_timer context manager (contextmanager, Generator, perf_counter) and the updated solve_instance signature (Iterable).


33-50: LGTM!

The task_timer context manager is well-implemented with silent as keyword-only (addressing prior feedback). The finally block correctly ensures elapsed time is logged even when exceptions occur within the timed block.


147-158: LGTM!

The task_timer integration is clean, and the stats gathering using sum() with generator expressions is idiomatic and efficient.


162-172: LGTM!

The directory creation logic is now idempotent with mkdir(parents=True, exist_ok=True), and the redundant exists() check has been removed (addressing prior feedback).


231-243: LGTM!

The suffix validation logic correctly filters invalid suffixes using set operations and provides helpful warning messages. The type change from list[str] to Iterable[str] increases flexibility while maintaining safety through internal conversion.


281-333: LGTM!

The check_solve_status function provides robust solver-agnostic status extraction with sensible fallback hierarchy. The multiple attribute checks handle both legacy Pyomo solver results and APPSI solver formats gracefully.


343-351: LGTM!

The write_results call is now simplified using the conditional expression for results_with_duals (addressing prior feedback), eliminating the duplicate branch.


365-394: LGTM!

The Graphviz generation is properly wrapped in a try/except with exc_info=True for full traceback logging, ensuring plotting failures don't crash the main workflow.


208-229: Solver configuration parameters verified as correct.

The CPLEX and Gurobi parameter values match PyPSA-Eur defaults exactly. CPLEX parameters use space-separated interactive names (barrier convergetol, feasopt tolerance) which is the correct convention for Pyomo's CPLEX solver interface. CBC and appsi_highs branches are appropriately left as no-ops.

@jdecarolis
Copy link
Collaborator

A couple nitpicky requests:

  1. Is it possible to report the times out to the left of "INFO" in square brackets, similar to (what I think are) the start and final stop times in the "new sample output"?
  2. If the answer to (1) is "no", is it possible to replace "Taken" with "Time" or Duration"?

@ParticularlyPythonicBS
Copy link
Member Author

ParticularlyPythonicBS commented Dec 8, 2025

1. Is it possible to report the times out to the left of "INFO" in square brackets, similar to (what I think are) the start and final stop times in the "new sample output"?

It is possible to report it for every step, currently it only displays if there is a change in the displayed time, so when the second changes, it is shown. I thought it would be easier to spot the changes that way. The log files have the time info on every line this is a console output customization, that I can change if preferred

2. If the answer to (1) is "no", is it possible to replace "Taken" with "Time" or Duration"?

Sounds good can change the wording there, changed to Time taken:

@ParticularlyPythonicBS ParticularlyPythonicBS force-pushed the ref/task_timer_context_manager branch from 323da03 to c3e1347 Compare December 8, 2025 23:01
@jdecarolis
Copy link
Collaborator

OK, that sounds great, thanks!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
temoa/_internal/run_actions.py (1)

175-279: Solver configuration and status handling are solid; address minor suffix/docs/logging nits

The refactored solve_instance plus check_solve_status provide clearer solver‑specific configuration, suffix whitelisting, and robust status extraction across legacy and APPSI-style results. A few small consistency issues are worth tightening up:

  1. Suffix name/doc mismatch and warning clarity (minor functional/doc issue)
    The docstring states that 'duals' is supported, but legit_suffixes contains 'dual', 'slack', and 'rc'. Callers passing 'duals' would be warned and filtered out despite the docstring. Consider aligning the docs and warning with the actual accepted set, for example:

  • :param solver_suffixes: iterable of string names for suffixes. See pyomo dox. right now, only
  • 'duals' is supported in the Temoa Framework. Some solvers may not support duals.
  • :param solver_suffixes: iterable of string names for suffixes. See Pyomo docs. Right now,
  • 'dual', 'slack', and 'rc' are recognized in the Temoa framework. Some solvers may not support
  • all suffixes.

And make the warning more explicit:

```diff
-        if bad_apples:
-            logger.warning(
-                'Solver suffix %s is not in pyomo standards (see pyomo dox).  Removed',
-                bad_apples,
-            )
+        if bad_apples:
+            logger.warning(
+                'Ignoring unsupported solver suffix(es) %s; supported suffixes are %s',
+                bad_apples,
+                legit_suffixes,
+            )
  1. Redundant exception argument to logger.exception (already flagged by tooling)
    On the RuntimeError path, logger.exception('Solver failed to solve and returned an error: %s', error) passes the caught exception as a formatting argument, but logger.exception will already attach the traceback and exception info. You can drop the %s and argument:

  •    except RuntimeError as error:
    
  •        logger.exception('Solver failed to solve and returned an error: %s', error)
    
  •    except RuntimeError as error:
    
  •        logger.exception('Solver failed to solve and returned an error')
    
    This also satisfies Ruff’s TRY401 warning.  
    
    
    
  1. Optional: make silent keyword‑only to mirror task_timer and appease FBT
    If you want to follow the same pattern as task_timer and Ruff’s FBT rules, you could make silent keyword‑only in solve_instance (after auditing call sites):

  • def solve_instance(
  • instance: TemoaModel,
    
  • solver_name: str,
    
  • silent: bool = False,
    
  • solver_suffixes: Iterable[str] | None = None,
    
  • ) -> tuple[TemoaModel, SolverResults]:
  • def solve_instance(
  • instance: TemoaModel,
    
  • solver_name: str,
    
  • *,
    
  • silent: bool = False,
    
  • solver_suffixes: Iterable[str] | None = None,
    
  • ) -> tuple[TemoaModel, SolverResults]:
    Since this function lives under `_internal`, the break risk is limited but still worth checking for any positional callers.  
    
    
    

Overall, the solve/timing and status‑logging behavior looks correct; these are just polish items.

Also applies to: 281-334

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 323da03 and c3e1347.

📒 Files selected for processing (2)
  • temoa/_internal/run_actions.py (9 hunks)
  • temoa/cli.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during code reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in the database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
🧬 Code graph analysis (1)
temoa/_internal/run_actions.py (1)
temoa/_internal/table_writer.py (2)
  • TableWriter (77-658)
  • write_results (91-135)
🪛 Ruff (0.14.7)
temoa/_internal/run_actions.py

178-178: Boolean-typed positional argument in function definition

(FBT001)


178-178: Boolean default positional argument in function definition

(FBT002)


255-255: Redundant exception object included in logging.exception call

(TRY401)


256-259: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


263-263: Do not catch blind exception: Exception

(BLE001)


265-268: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


269-269: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: setup and test (windows-latest, 3.12)
  • GitHub Check: setup and test (macos-latest, 3.12)
  • GitHub Check: setup and test (macos-latest, 3.13)
  • GitHub Check: setup and test (windows-latest, 3.13)
🔇 Additional comments (4)
temoa/cli.py (1)

49-55: RichHandler timestamp configuration aligns with requested CLI format

Enabling show_time=True with log_time_format='[%X]' should emit timestamps like [HH:MM:SS] INFO … on every console log line, matching the reviewer’s request while keeping file logging unchanged. Please run a simple command (e.g., temoa --help) to visually confirm the Rich output on your terminal.

temoa/_internal/run_actions.py (3)

33-50: task_timer is exception-safe and matches the new logging pattern

Using perf_counter() with a try/finally ensures the “Finished … (Time taken: …s)” message is logged even if the block raises, while silent cleanly gates both start/finish INFO logs. This pairs well with the Rich console timestamps configured in cli.py to produce consistent, per-step timing output. Please run a representative workflow to confirm the emitted lines look as expected with your terminal’s Rich formatting.


73-81: Database version check now closes connections reliably; confirm metadata schema

Wrapping each (con, name) pair in a try/…/finally with con.close() ensures both input and (optional) output database connections are closed regardless of query success or failure, resolving prior leak concerns. The queries now assume a metadata table with value entries for DB_MAJOR and DB_MINOR; please confirm this matches the actual schema for both input and output databases so version checking behaves as intended.

Also applies to: 87-104


343-360: Result processing refactor and logging improvements look correct

Wrapping result handling in task_timer('Processing results', silent=config.silent) plus the unified write_results call with results_with_duals=results if config.save_duals else None cleanly ties dual writing to the config flag and matches the TableWriter.write_results API. The revised scenario_name / temp_scenario logic for Excel output and the more explicit Total Cost value: … INFO log are also consistent and low‑risk. Please confirm that make_excel and downstream tooling expect the scenario container and filename in this shape (single‑element set and path without extension).

Also applies to: 361-364

@ParticularlyPythonicBS ParticularlyPythonicBS force-pushed the ref/task_timer_context_manager branch from c3e1347 to f4e7082 Compare December 11, 2025 20:40
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
temoa/_internal/run_actions.py (2)

247-278: Redundant exception parameter in logging.

Line 255 passes the caught error to logger.exception, but logging.exception automatically includes the exception and traceback. A past review comment indicated this was addressed in commit c3e1347, but the redundant parameter is still present.

Apply this fix:

         except RuntimeError as error:
-            logger.exception('Solver failed to solve and returned an error: %s', error)
+            logger.exception('Solver failed to solve and returned an error')
             logger.error(

The subsequent logger.error calls (lines 256-259, 265-268) are appropriate for adding context without re-logging the traceback. The blind Exception catch at line 263 is acceptable for robust status extraction.


178-178: Consider making silent keyword-only for consistency.

The silent boolean parameter is positional, while task_timer (line 34) uses a keyword-only silent parameter (via *). Making silent keyword-only here would maintain API consistency and avoid boolean trap anti-patterns flagged by static analysis.

 def solve_instance(
     instance: TemoaModel,
     solver_name: str,
+    *,
     silent: bool = False,
     solver_suffixes: Iterable[str] | None = None,
 ) -> tuple[TemoaModel, SolverResults]:

All current call sites should already use silent= by name, so this would be non-breaking if the codebase follows that convention.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3e1347 and f4e7082.

📒 Files selected for processing (2)
  • temoa/_internal/run_actions.py (9 hunks)
  • temoa/cli.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during code reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in the database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
🪛 Ruff (0.14.8)
temoa/_internal/run_actions.py

178-178: Boolean-typed positional argument in function definition

(FBT001)


178-178: Boolean default positional argument in function definition

(FBT002)


255-255: Redundant exception object included in logging.exception call

(TRY401)


256-259: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


263-263: Do not catch blind exception: Exception

(BLE001)


265-268: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


269-269: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: setup and test (macos-latest, 3.12)
  • GitHub Check: setup and test (macos-latest, 3.13)
  • GitHub Check: setup and test (windows-latest, 3.12)
  • GitHub Check: setup and test (windows-latest, 3.13)
🔇 Additional comments (7)
temoa/cli.py (1)

53-53: Console timestamps enabled as requested.

The addition of show_time=True aligns with the PR objectives and user feedback to display timestamps in square brackets for every step in console output. This makes console behavior consistent with the "Time taken:" labels emitted by the new task_timer context manager.

temoa/_internal/run_actions.py (6)

33-50: task_timer implementation is solid.

The context manager correctly uses perf_counter for timing, ensures the elapsed time is logged even when exceptions occur (via finally), and the keyword-only silent parameter prevents boolean traps. The log format matches the PR objectives.


147-159: Model building with task_timer looks good.

The task_timer wrapper provides consistent "Started"/"Finished" logging for model instantiation, and using sum() with generator expressions for constraint/variable counting is both concise and efficient. The aggregate log message gives clear build metrics.


162-173: LP file saving is correct.

The idempotent mkdir(parents=True, exist_ok=True) call ensures the directory structure exists before writing, and the str(filename) cast accommodates Pyomo's API requirements.


208-243: Solver configuration and suffix validation are well-structured.

The explicit solver configuration branches for CBC, CPLEX, Gurobi, and APPSI HiGHS are clear and well-documented with parameter sources. The suffix validation logic (lines 232-243) correctly filters invalid suffixes against the legitimate set {'dual', 'slack', 'rc'} and warns about removals, providing good user feedback.


281-333: Robust solver-agnostic status extraction.

The check_solve_status function provides excellent compatibility across solver interfaces with multiple fallback strategies: it tries legacy result['Solution'].Status, then APPSI-style attributes (status, problem_status, solver_status), and finally a string representation fallback. The safe attribute checking with hasattr prevents crashes on unexpected result formats.


336-394: Results handling refactored cleanly.

The task_timer wrapper provides consistent timing for result processing, and the conditional results_with_duals=results if config.save_duals else None parameter elegantly handles both cases without code duplication. The Graphviz generation is appropriately wrapped in try/except (lines 393-394) to ensure optional visualization failures don't crash the main workflow.

@ParticularlyPythonicBS ParticularlyPythonicBS force-pushed the ref/task_timer_context_manager branch 2 times, most recently from 230986a to 4eefc48 Compare December 18, 2025 19:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
temoa/_internal/run_actions.py (1)

361-387: Potential resource leak in Graphviz generation.

If an exception occurs between graph_gen.connect() (line 376) and graph_gen.close() (line 386), the connection may not be closed. Consider using a context manager pattern or explicit try/finally.

Suggested fix:
             graph_gen = GraphvizDiagramGenerator(
                 db_file=str(config.output_database),
                 scenario=config.scenario,
                 out_dir=out_dir,
                 verbose=0,  # Less verbose for integrated run
             )
             graph_gen.connect()
-
-            # Get periods from the model instance
-            periods = sorted(instance.time_optimize)
-
-            for period in periods:
-                # Generate main results diagram for the period
-                # We pass None for region to generate for all/default
-                graph_gen.create_main_results_diagram(period=period, region=None)
-
-            graph_gen.close()
+            try:
+                # Get periods from the model instance
+                periods = sorted(instance.time_optimize)
+
+                for period in periods:
+                    # Generate main results diagram for the period
+                    # We pass None for region to generate for all/default
+                    graph_gen.create_main_results_diagram(period=period, region=None)
+            finally:
+                graph_gen.close()
             logger.info('Graphviz plots generated in %s', graph_gen.out_dir)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 230986a and 4eefc48.

📒 Files selected for processing (2)
  • temoa/_internal/run_actions.py (10 hunks)
  • temoa/cli.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during code reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
📚 Learning: 2025-11-03T13:34:35.907Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 184
File: temoa/data_processing/database_util.py:109-118
Timestamp: 2025-11-03T13:34:35.907Z
Learning: In the Temoa project, SQL injection vulnerabilities in the database utility classes (such as temoa/data_processing/database_util.py) are considered low concern due to the data access patterns and should not be flagged during reviews. Technical debt for SQL injection hardening should be tracked separately as issues rather than blocking PRs.

Applied to files:

  • temoa/_internal/run_actions.py
🧬 Code graph analysis (1)
temoa/_internal/run_actions.py (1)
temoa/_internal/table_writer.py (1)
  • write_results (231-274)
🪛 Ruff (0.14.8)
temoa/_internal/run_actions.py

174-174: Boolean-typed positional argument in function definition

(FBT001)


174-174: Boolean default positional argument in function definition

(FBT002)


251-251: Redundant exception object included in logging.exception call

(TRY401)


252-255: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


259-259: Do not catch blind exception: Exception

(BLE001)


261-264: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


265-265: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: setup and test (windows-latest, 3.12)
  • GitHub Check: setup and test (macos-latest, 3.13)
  • GitHub Check: setup and test (macos-latest, 3.12)
  • GitHub Check: setup and test (windows-latest, 3.13)
🔇 Additional comments (8)
temoa/cli.py (1)

49-55: LGTM! Console timestamps enabled as requested.

The show_time=True setting combined with the existing log_time_format='[%X]' will produce timestamps in [HH:MM:SS] format to the left of the log level, aligning with the PR objectives for consistent CLI output.

temoa/_internal/run_actions.py (7)

33-50: LGTM! Clean task_timer implementation.

The context manager correctly uses perf_counter for timing, makes silent keyword-only (addressing prior feedback), and always logs elapsed time in the finally block even when exceptions occur. The "Time taken:" wording aligns with the PR discussion.


80-103: Connection lifecycle handling is correct.

Each database connection is now properly scoped within the loop iteration's try/finally block, ensuring connections are closed even if queries fail. This addresses the previously identified edge-case leak concern.


143-155: LGTM! Clean integration of task_timer with model building.

The task_timer wrapper provides standardized timing, and the aggregate counts using sum(len(...)) over component objects give clear, low-overhead build metrics.


158-168: LGTM!

The mkdir(parents=True, exist_ok=True) call is now idempotent without the redundant exists() check, addressing previous feedback. The defensive if not lp_path guard is reasonable even if callers currently validate.


204-239: Solver configuration and suffix handling look good.

The solver-specific options are well-documented with references to PyPSA. The suffix validation correctly filters invalid entries and warns users, preventing silent failures when requesting unsupported suffixes.


277-329: LGTM! Robust solver-agnostic status extraction.

The function gracefully handles both legacy and APPSI solver result formats with multiple fallback strategies, ensuring status information is always available for logging even when result object structures vary between solvers.


339-347: LGTM! Simplified write_results call.

The conditional results_with_duals=results if config.save_duals else None consolidates the previous two-branch logic into a single call, as suggested in prior review feedback.

Comment on lines 250 to +265
except RuntimeError as error:
logger.error('Solver failed to solve and returned an error: %s', error)
logger.exception('Solver failed to solve and returned an error: %s', error)
logger.error(
'This may be due to asking for suffixes (duals) for an incompatible solver. '
"Try de-selecting 'save_duals' in the config. (see note in run_actions.py code)"
)
if result:
try:
_ok, status_msg = check_solve_status(result)
except Exception:
status_msg = '<unable to extract status>'
logger.error(
'Solver reported termination condition (if any): %s', result['Solution'].Status
'Solver reported termination/status (if any): %s',
status_msg,
)
sys.stderr.write('solver failure. See log file.')
sys.exit(-1)
raise RuntimeError('Solver failure. See log file.') from error
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Redundant exception object in logger.exception call.

On line 251, logger.exception already captures and logs the exception automatically, so passing error as a formatting argument is redundant. This was flagged in a past review as addressed but appears to persist.

Suggested fix:
         except RuntimeError as error:
-            logger.exception('Solver failed to solve and returned an error: %s', error)
+            logger.exception('Solver failed to solve and returned an error')
             logger.error(

The bare Exception catch at line 259 is acceptable here since it's defensive status extraction within an already-failed code path.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
except RuntimeError as error:
logger.error('Solver failed to solve and returned an error: %s', error)
logger.exception('Solver failed to solve and returned an error: %s', error)
logger.error(
'This may be due to asking for suffixes (duals) for an incompatible solver. '
"Try de-selecting 'save_duals' in the config. (see note in run_actions.py code)"
)
if result:
try:
_ok, status_msg = check_solve_status(result)
except Exception:
status_msg = '<unable to extract status>'
logger.error(
'Solver reported termination condition (if any): %s', result['Solution'].Status
'Solver reported termination/status (if any): %s',
status_msg,
)
sys.stderr.write('solver failure. See log file.')
sys.exit(-1)
raise RuntimeError('Solver failure. See log file.') from error
except RuntimeError as error:
logger.exception('Solver failed to solve and returned an error')
logger.error(
'This may be due to asking for suffixes (duals) for an incompatible solver. '
"Try de-selecting 'save_duals' in the config. (see note in run_actions.py code)"
)
if result:
try:
_ok, status_msg = check_solve_status(result)
except Exception:
status_msg = '<unable to extract status>'
logger.error(
'Solver reported termination/status (if any): %s',
status_msg,
)
raise RuntimeError('Solver failure. See log file.') from error
🧰 Tools
🪛 Ruff (0.14.8)

251-251: Redundant exception object included in logging.exception call

(TRY401)


252-255: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


259-259: Do not catch blind exception: Exception

(BLE001)


261-264: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


265-265: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In temoа/_internal/run_actions.py around lines 250 to 265, the logger.exception
call includes the caught exception as a formatting argument which is redundant
because logger.exception already logs the traceback and exception; remove the
extra "%s" and the error argument and instead call logger.exception with a plain
descriptive message (e.g. "Solver failed to solve and returned an error") so the
exception is still logged with traceback, leaving the following logger.error
messages and the raised RuntimeError intact.

@ParticularlyPythonicBS ParticularlyPythonicBS force-pushed the ref/task_timer_context_manager branch from 4eefc48 to e637077 Compare December 18, 2025 19:10
@ParticularlyPythonicBS ParticularlyPythonicBS merged commit c855830 into unstable Dec 18, 2025
9 checks passed
@ParticularlyPythonicBS ParticularlyPythonicBS deleted the ref/task_timer_context_manager branch December 18, 2025 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants