⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Conversation

@ParticularlyPythonicBS
Copy link
Member

@ParticularlyPythonicBS ParticularlyPythonicBS commented Jan 12, 2026

adds typing to the utilities sub dir

Summary by CodeRabbit

  • Type Safety & Quality

    • Expanded type annotations across utilities for better IDE support and more robust resource handling.
  • API Enhancements

    • Added verbosity option to graph export, tightened migration function signatures, and added a utility to detect database files.
  • New Features

    • Capacity analysis now renders a histogram visualization.
  • Bug Fixes

    • Safer migration and data-processing flows with additional guards and empty-data fallbacks.

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

Walkthrough

Narrowed mypy exclusions to include temoa/utilities, and applied widespread type annotations, minor control-flow guards, a few signature refinements, and small behavior-preserving edits across utility modules (DB access, migrations, graph conversion, plotting, and model-cost exploration).

Changes

Cohort / File(s) Summary
Configuration
pyproject.toml
Removed ^temoa/utilities/ from the mypy exclude pattern so utilities are now included in type checking.
Capacity analyzer
temoa/utilities/capacity_analyzer.py
Switched from PROJECT_ROOT/os.path to a direct source_db_file string, added explicit local type hints, used context manager for sqlite connections, added plotting (plt.hist/plt.show()), and tightened result handling.
DB output management
temoa/utilities/clear_db_outputs.py
Added explicit type annotations to module-level variables: basic_output_tables, optional_output_tables, target_db_str, and proceed.
Database utilities
temoa/utilities/database_util.py
Added future/typing imports and casts, safer close() guards, casted query returns, broadened get_capacity_for_tech_and_period return type, and added public is_database_file() static method.
Migrations (multiple)
temoa/utilities/db_migration_to_v3.py, temoa/utilities/db_migration_v3_to_v3_1.py, temoa/utilities/db_migration_v3_1_to_v4.py, temoa/utilities/sql_migration_v3_1_to_v4.py, temoa/utilities/run_all_v4_migrations.py
Widespread typing additions for connections/cursors and data containers, some signatures narrowed to argparse.Namespace, get_table_info/tuple types made explicit, defensive error paths (returning empty lists / continuing on missing tables), and run_command annotated to return CompletedProcess[str].
Graph utilities
temoa/utilities/graph_utils.py
convert_graph_to_json gained a verbosity: int parameter and explicit return type; runtime GraphType TypeVar formatting adjusted (semantics unchanged).
Unit cost explorer
temoa/utilities/unit_cost_explorer.py
Added TYPE_CHECKING guarded imports, many explicit casts and type annotations for indices/variables and model access, and typed intermediate values used in cost calculations and constraints (logic preserved).

Sequence Diagram(s)

(omitted — changes are typing/guards and small signature tweaks, not new multi-component control flows)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Maintenance

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 'adding types to utilities' directly and accurately describes the main objective of the pull request, which adds type annotations across the utilities subdirectory.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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: 1

Caution

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

⚠️ Outside diff range comments (2)
temoa/utilities/db_migration_v3_1_to_v4.py (1)

97-101: Consider consolidating duplicate get_table_info function.

This function is identical to the one in sql_migration_v3_1_to_v4.py (lines 120-124). Both files also share CUSTOM_MAP, to_snake_case, and map_token_no_cascade. Consider extracting these common utilities to a shared module to reduce duplication and ensure consistent behavior.

temoa/utilities/unit_cost_explorer.py (1)

157-163: Typo in variable name: mulitplier should be multiplier.

Proposed fix
-mulitplier: float = (
+multiplier: float = (
     storage_dur
     * model.segment_fraction_per_season[2020, 'winter']
     * model.days_per_period
     * c2a
     * c
 )
-print(f'The multiplier for the storage should be: {mulitplier}')
+print(f'The multiplier for the storage should be: {multiplier}')
🤖 Fix all issues with AI agents
In @temoa/utilities/unit_cost_explorer.py:
- Around line 141-142: Two duplicate model construction calls are present:
remove the redundant model.v_storage_level.construct() and
model.segment_fraction_per_season.construct() occurrences (the second calls
around the shown diff) because these are already invoked earlier; keep only the
original construct() calls for v_storage_level and segment_fraction_per_season
and delete the repeated lines to avoid unintended double construction.
📜 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 4b49909 and 1cd2091.

📒 Files selected for processing (11)
  • pyproject.toml
  • temoa/utilities/capacity_analyzer.py
  • temoa/utilities/clear_db_outputs.py
  • temoa/utilities/database_util.py
  • temoa/utilities/db_migration_to_v3.py
  • temoa/utilities/db_migration_v3_1_to_v4.py
  • temoa/utilities/db_migration_v3_to_v3_1.py
  • temoa/utilities/graph_utils.py
  • temoa/utilities/run_all_v4_migrations.py
  • temoa/utilities/sql_migration_v3_1_to_v4.py
  • temoa/utilities/unit_cost_explorer.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-27T15:53:41.829Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 177
File: temoa/model_checking/commodity_network.py:26-33
Timestamp: 2025-10-27T15:53:41.829Z
Learning: The Temoa project requires Python 3.12 or above as the minimum supported version, so PEP 695 `type` syntax for type aliases is appropriate and preferred over `TypeAlias`.

Applied to files:

  • temoa/utilities/db_migration_to_v3.py
🧬 Code graph analysis (3)
temoa/utilities/db_migration_v3_1_to_v4.py (1)
temoa/utilities/sql_migration_v3_1_to_v4.py (1)
  • get_table_info (120-124)
temoa/utilities/sql_migration_v3_1_to_v4.py (1)
tests/utilities/namespace_mock.py (1)
  • Namespace (1-3)
temoa/utilities/capacity_analyzer.py (1)
temoa/utilities/database_util.py (1)
  • close (59-64)
🪛 Ruff (0.14.10)
temoa/utilities/db_migration_v3_to_v3_1.py

173-173: Possible SQL injection vector through string-based query construction

(S608)


195-195: Possible SQL injection vector through string-based query construction

(S608)


220-220: Possible SQL injection vector through string-based query construction

(S608)


231-233: Possible SQL injection vector through string-based query construction

(S608)


280-280: Possible SQL injection vector through string-based query construction

(S608)

temoa/utilities/db_migration_to_v3.py

139-139: Possible SQL injection vector through string-based query construction

(S608)

⏰ 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.13)
  • GitHub Check: setup and test (windows-latest, 3.12)
🔇 Additional comments (22)
pyproject.toml (1)

144-144: LGTM!

The mypy exclusion pattern update correctly enables type checking for temoa/utilities/ while keeping temoa/extensions/ and stubs/ excluded. This aligns with the PR's goal of adding type annotations to the utilities directory.

temoa/utilities/clear_db_outputs.py (1)

10-30: LGTM!

The type annotations are correctly applied to the module-level variables and local variables. The list[str] annotations for the table name lists and str annotations for the command-line argument and user input are appropriate.

temoa/utilities/sql_migration_v3_1_to_v4.py (3)

27-27: LGTM!

The Any import is appropriate for typing the heterogeneous tuples returned by PRAGMA table_info.


120-124: LGTM!

The return type list[tuple[Any, ...]] accurately reflects that PRAGMA table_info returns tuples with mixed types (cid, name, type, notnull, default_value, pk).


127-127: No test compatibility issue exists. The migrate_dump_to_sqlite function has no tests in the codebase. The custom Namespace class in tests/utilities/namespace_mock.py is used only in tests/test_exchange_cost_ledger.py for different functionality, not for testing this function. Additionally, the custom Namespace class would be compatible with the function's interface due to its duck-typed attribute access pattern (the function only accesses args.input, args.schema, args.output, and args.debug).

temoa/utilities/db_migration_v3_1_to_v4.py (2)

19-19: LGTM!

The Any import is needed for the heterogeneous tuple typing.


135-135: LGTM!

The argparse.Namespace type annotation correctly specifies the expected parameter type for CLI-driven execution.

temoa/utilities/capacity_analyzer.py (3)

44-57: Defensive resource cleanup is appropriate for this script.

The type annotations are correct. The if 'con' in locals() pattern works for script-style code since con is only defined if sqlite3.connect succeeds. Note that the relevant snippet from database_util.py uses hasattr(self, 'con') for class-based cleanup—both patterns are appropriate for their respective contexts.


60-82: LGTM!

The type annotations for the data processing variables (caps, small_cap_sources, large_cap_sources, total_cap, cumulative_caps) are accurate and improve code clarity.


23-41: Robust package resource discovery with correct fallback.

The use of importlib.resources.files for locating data files is a good improvement for package installations. The fallback path correctly handles the actual repository structure where the script is at temoa/utilities/capacity_analyzer.py and data_files is at the repository root, using current_file.parents[2] to traverse up to the root level.

temoa/utilities/unit_cost_explorer.py (1)

6-7: Type annotations look good.

The added type hints improve code clarity and type safety. The use of cast('Any', ...) for Pyomo model dynamic attributes is appropriate since Pyomo uses runtime attribute assignment that mypy cannot statically verify.

Also applies to: 34-35, 71-76, 79-80, 87-96

temoa/utilities/db_migration_to_v3.py (2)

248-248: Good fix for operator precedence.

The parentheses in not (common ^ techs) correctly ensure the symmetric difference is computed before negation. The previous expression not common ^ techs would have been interpreted as (not common) ^ techs, which would produce incorrect results.


40-42: Type annotations improve code clarity.

The added type hints for sqlite3.Connection, sqlite3.Cursor, and the various list/dict structures are appropriate and improve maintainability.

Regarding the static analysis SQL injection warnings (S608): These are false positives in this context. This is an internal migration tool that constructs queries using trusted table/column names from the database schema itself, not from external user input. The dynamic SQL is necessary for the table migration logic.

Also applies to: 54-56, 109-120, 130-143, 230-234, 366-372

temoa/utilities/db_migration_v3_to_v3_1.py (3)

14-15: Improved import pattern.

Assuming temoa is installed in the environment is cleaner than the previous dynamic sys.path manipulation. This aligns with standard Python packaging practices.


278-316: Pandas usage for period indexing is appropriate.

Using pd.DataFrame for the complex period calculation logic improves readability compared to pure list/dict manipulation. The typing with pd.DataFrame and proper guards (len(df_data) == 0) are well-structured.


39-41: Type annotations are consistent and well-applied.

The variable renaming from generic data to more descriptive names like data_rows, data_transformed, and df_data improves code clarity. The explicit type hints for list[Any], dict[...], and sqlite3.* types are appropriate.

Regarding SQL injection warnings (S608): These are false positives. This migration script uses table and column names from the database schema itself, not external user input.

Also applies to: 78-120, 173-196, 238-260, 373-395, 423-430

temoa/utilities/run_all_v4_migrations.py (1)

26-36: Accurate return type annotation.

The subprocess.CompletedProcess[str] return type is correct since the function passes text=True to subprocess.run(), making stdout and stderr return str instead of bytes.

temoa/utilities/database_util.py (3)

59-64: Good defensive programming in close() method.

The hasattr guards handle edge cases where the object may not be fully initialized (e.g., if an exception occurs during __init__ before con or cur are assigned). This prevents AttributeError during cleanup.


133-133: Appropriate use of cast() for type consistency.

The cast() calls ensure the return types match the declared signatures (set[str], set[tuple[str, str]]) when SQLite returns generic row tuples. This is a clean way to bridge the gap between SQLite's dynamic typing and Python's static type hints.

Also applies to: 179-179, 195-195


197-199: Return type accurately reflects method behavior.

The widened return type pd.DataFrame | pd.Series[Any] correctly reflects that the method returns a Series (via result.sum() on line 229) when certain conditions are met, and a DataFrame otherwise.

temoa/utilities/graph_utils.py (2)

40-46: Multi-line TypeVar formatting is consistent.

The reformatted GraphType TypeVar maintains the same type constraints while improving readability. The runtime definition (without generic parameters) is necessary because NetworkX graph types cannot be subscripted at runtime.


49-54: Function signature updated with verbosity parameter.

The noqa: UP047 directive is appropriate to suppress the Ruff rule that suggests using PEP 695 type syntax, which would cause runtime errors with NetworkX's generic graph types.

The new verbosity parameter enables conditional debug logging (used on lines 72 and 98). All callers in temoa/utilities/visualizer.py have been updated to pass the verbosity argument (passing 0 in both cases).

Comment on lines 141 to 146
model.v_storage_level.construct()
model.segment_fraction_per_season.construct()
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

Duplicate construct() calls detected.

model.v_storage_level.construct() and model.segment_fraction_per_season.construct() are already called on lines 138-139. These duplicate calls are likely unintentional and should be removed.

Proposed fix
-model.v_storage_level.construct()
-model.segment_fraction_per_season.construct()
-
 cast('Any', model.is_seasonal_storage)['battery'] = False
📝 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
model.v_storage_level.construct()
model.segment_fraction_per_season.construct()
cast('Any', model.is_seasonal_storage)['battery'] = False
🤖 Prompt for AI Agents
In @temoa/utilities/unit_cost_explorer.py around lines 141 - 142, Two duplicate
model construction calls are present: remove the redundant
model.v_storage_level.construct() and
model.segment_fraction_per_season.construct() occurrences (the second calls
around the shown diff) because these are already invoked earlier; keep only the
original construct() calls for v_storage_level and segment_fraction_per_season
and delete the repeated lines to avoid unintended double construction.

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: 7

Caution

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

⚠️ Outside diff range comments (1)
temoa/utilities/database_util.py (1)

112-123: Consider fixing the method name typo.

The method is named get_time_peridos_for_flags but should be get_time_periods_for_flags. This appears to be pre-existing, but worth fixing while you're updating this file.

The int() cast with explanatory comment is a good addition.

🤖 Fix all issues with AI agents
In @temoa/utilities/capacity_analyzer.py:
- Around line 55-57: The cleanup uses a non-idiomatic "if 'con' in locals()"
check; instead initialize the connection variable (con = None) before the try in
the function/method that creates the DB connection and then in the finally block
check "if con is not None: con.close()"; update references to the connection
variable (con) accordingly so the finally block reliably closes only an actual
connection.
- Around line 23-41: Update the misleading fallback comment near the fallback
block that builds source_db_file: clarify that current_file =
Path(__file__).resolve() refers to temoa/utilities/capacity_analyzer.py and that
.parents[2] climbs to the repository root where data_files/untracked_data/db
resides (i.e., "# temoa/utilities/capacity_analyzer.py -> data_files (at repo
root)"). Keep the existing .parents[2] logic and variable names (current_file,
source_db_file) unchanged.

In @temoa/utilities/database_util.py:
- Line 133: The cast usage in the return expression of the method that does
"return {cast('str', row[0]) for row in self.cur.execute(query)}" should use a
real type object instead of a string literal; change the cast call to cast(str,
row[0]) (no quotes) in that set comprehension so it uses the actual type for
Python 3.12+ type narrowing while leaving the rest of the expression
(self.cur.execute(query)) unchanged.

In @temoa/utilities/db_migration_v3_to_v3_1.py:
- Around line 244-254: The lifetime_process annotation is overly broad; change
its type from dict[tuple[Any, ...], float] to a more specific tuple type
reflecting the keys used (region, tech, vintage). Update the declaration of
lifetime_process to use either dict[tuple[str, str, int], float] if vintage is
an int, or dict[tuple[str, str, Any], float] if vintage can vary, keeping use of
TemoaModel.default_lifetime_tech and the subsequent assignments unchanged.
- Around line 283-285: Replace the idiomatic DataFrame emptiness check: in the
block that currently does "if len(df_data) == 0: print('No data for: ' +
old_name) continue", use the pandas property "df_data.empty" instead (i.e., "if
df_data.empty: ...") so the check is clearer and more idiomatic while preserving
the existing print and continue behavior; locate the snippet referencing df_data
and old_name and make the swap.

In @temoa/utilities/unit_cost_explorer.py:
- Around line 38-39: Replace the inline tuple type annotations for the recurring
index tuples with PEP 695 type aliases: create type RTV = tuple[str, str, int]
and type RPTV = tuple[str, int, str, int], then change the variable annotations
for rtv and rptv to use RTV and RPTV respectively so the intent is clearer and
the aliases can be reused across the module.
- Around line 142-146: The two calls to model.v_storage_level.construct() and
model.segment_fraction_per_season.construct() are duplicated; remove the
redundant second occurrences so each of model.v_storage_level.construct() and
model.segment_fraction_per_season.construct() is invoked only once (keep the
first calls and delete the repeated calls that follow).
📜 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 1cd2091 and ebc2516.

📒 Files selected for processing (11)
  • pyproject.toml
  • temoa/utilities/capacity_analyzer.py
  • temoa/utilities/clear_db_outputs.py
  • temoa/utilities/database_util.py
  • temoa/utilities/db_migration_to_v3.py
  • temoa/utilities/db_migration_v3_1_to_v4.py
  • temoa/utilities/db_migration_v3_to_v3_1.py
  • temoa/utilities/graph_utils.py
  • temoa/utilities/run_all_v4_migrations.py
  • temoa/utilities/sql_migration_v3_1_to_v4.py
  • temoa/utilities/unit_cost_explorer.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 177
File: temoa/model_checking/commodity_network.py:26-33
Timestamp: 2025-10-27T15:53:41.829Z
Learning: The Temoa project requires Python 3.12 or above as the minimum supported version, so PEP 695 `type` syntax for type aliases is appropriate and preferred over `TypeAlias`.
🧬 Code graph analysis (4)
temoa/utilities/db_migration_v3_1_to_v4.py (2)
temoa/utilities/sql_migration_v3_1_to_v4.py (1)
  • get_table_info (120-124)
tests/utilities/namespace_mock.py (1)
  • Namespace (1-3)
temoa/utilities/capacity_analyzer.py (1)
temoa/utilities/database_util.py (1)
  • close (59-64)
temoa/utilities/unit_cost_explorer.py (1)
temoa/components/storage.py (1)
  • storage_energy_upper_bound_constraint (219-273)
temoa/utilities/database_util.py (1)
temoa/utilities/graphviz_generator.py (1)
  • close (189-194)
🪛 Ruff (0.14.10)
temoa/utilities/db_migration_v3_to_v3_1.py

173-173: Possible SQL injection vector through string-based query construction

(S608)


195-195: Possible SQL injection vector through string-based query construction

(S608)


220-220: Possible SQL injection vector through string-based query construction

(S608)


231-233: Possible SQL injection vector through string-based query construction

(S608)


280-280: Possible SQL injection vector through string-based query construction

(S608)

temoa/utilities/db_migration_to_v3.py

139-139: Possible SQL injection vector through string-based query construction

(S608)

⏰ 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.13)
  • 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)
🔇 Additional comments (36)
temoa/utilities/graph_utils.py (2)

40-46: LGTM!

The multi-line formatting with trailing commas improves readability and maintains consistency with the TYPE_CHECKING block above.


49-54: The noqa: UP047 suppression is correct.

UP047 (non-pep695-generic-function) is a valid ruff rule that suggests converting standalone TypeVar/ParamSpec/TypeVarTuple style generics to PEP 695 inline type-parameter syntax. The suppression is appropriate here to preserve networkx compatibility, as converting the GraphType parameter to PEP 695 syntax could cause runtime type errors with networkx generics.

pyproject.toml (1)

144-144: LGTM!

The narrowed exclusion pattern correctly enables type checking for temoa/utilities/ while maintaining exclusions for temoa/extensions/ and stubs/. This aligns with the PR objective of adding typing to the utilities subdirectory.

temoa/utilities/run_all_v4_migrations.py (1)

26-36: LGTM!

The return type subprocess.CompletedProcess[str] is correct since text=True is passed to subprocess.run, ensuring stdout/stderr are strings rather than bytes.

temoa/utilities/clear_db_outputs.py (1)

10-30: LGTM!

The type annotations are correct and use modern Python 3.12+ syntax (list[str] instead of List[str]), which is appropriate for this project. Based on learnings, Python 3.12+ is the minimum supported version.

temoa/utilities/sql_migration_v3_1_to_v4.py (3)

27-27: LGTM!

The Any import is correctly added to support the return type annotation.


120-124: LGTM!

The return type list[tuple[Any, ...]] is the correct annotation for SQLite's PRAGMA table_info results, which returns tuples with heterogeneous element types (cid, name, type, notnull, default_value, pk).


127-127: LGTM!

Adding the argparse.Namespace type annotation improves type safety for the function parameter.

temoa/utilities/unit_cost_explorer.py (4)

6-16: LGTM!

The TYPE_CHECKING guard pattern is correctly used to import type hints without runtime overhead. This is a clean approach for importing types from temoa.types.core_types.


148-157: Casting string/int literals to domain types is verbose but acceptable.

The multiple cast() calls for literal values like cast('Region', 'A') work for satisfying mypy but are quite verbose. Since this is a QA/exploration script rather than production code, this approach is acceptable. An alternative would be to define typed variables once and reuse them.


71-84: LGTM!

The use of cast('Any', ...) for mutable model attributes and explicit type annotations for tot_cost_expr and total_cost properly satisfy the type checker while maintaining the existing runtime behavior.


91-100: LGTM!

The explicit float type annotations for the storage calculation variables improve clarity and type safety. The unit conversion calculations are well-documented through variable naming.

temoa/utilities/db_migration_v3_1_to_v4.py (3)

19-19: LGTM!

The Any import is correctly added to support the typed return value of get_table_info.


97-101: LGTM!

The return type list[tuple[Any, ...]] correctly represents the heterogeneous PRAGMA table_info result and is consistent with the same function in sql_migration_v3_1_to_v4.py.


135-135: LGTM!

Explicit argparse.Namespace type annotation correctly documents the expected input type from parse_cli().

temoa/utilities/capacity_analyzer.py (3)

9-10: LGTM!

Imports correctly added to support Path operations and Any type annotation.


60-67: LGTM!

Type annotations for the data processing pipeline are accurate and improve code clarity.


79-82: LGTM!

Cumulative capacity calculation is correctly typed.

temoa/utilities/database_util.py (5)

10-16: LGTM!

Future annotations and typing imports correctly added to support enhanced type hints throughout the module.


59-64: LGTM!

The hasattr guards correctly protect against AttributeError if the constructor fails before initializing cur or con.


179-179: LGTM!

Same pattern as above - cast correctly narrows the type for the type checker.


195-195: LGTM!

Cast correctly specifies the tuple type for commodity-technology pairs.


197-199: LGTM!

The union return type pd.DataFrame | pd.Series[Any] correctly reflects that the method can return either type based on the conditional logic at lines 228-231.

temoa/utilities/db_migration_v3_to_v3_1.py (7)

9-15: LGTM!

The typing import and direct import from temoa.core.model (assuming installed package) is cleaner than dynamic path manipulation.


39-41: LGTM!

Connection and cursor type annotations are correct and consistent with the other migration scripts.


78-156: LGTM!

Type annotations for table mappings are accurate and improve code readability.


168-197: LGTM with note on SQL construction.

The SQL queries use f-strings with table names from the hardcoded operator_added_tables dictionary (not user input), so the static analysis SQL injection warning is a false positive in this context.

The defensive empty-data checks on lines 178-180 and 191-193 are good additions.


215-236: LGTM!

Type annotations correctly added. The SQL injection warnings are false positives since table/column names come from the hardcoded configuration.


373-395: LGTM!

Defensive handling for missing LoanLifetimeTech table with proper fallback to empty list.


423-430: LGTM!

FK validation with proper type annotations and error handling.

temoa/utilities/db_migration_to_v3.py (6)

15-15: LGTM!

Any import added to support type annotations.


40-42: LGTM!

Connection and cursor type annotations are consistent with the other migration scripts in this PR.


54-120: LGTM!

Type annotations for table mappings are accurate.


130-143: LGTM!

Column and data fetching with proper type annotations. The SQL injection warning is a false positive since table names come from the hardcoded configuration, not user input.


230-248: LGTM!

The rps_entries type annotation is correct. The boolean logic on line 248 correctly tracks whether all non-global groups share the same technologies by checking if the symmetric difference is empty.


366-366: LGTM!

Type annotation for new_data is correct.

Comment on lines 23 to 41
# Attempt to locate data_files within the installed package or nearby
try:
# Python 3.9+ preferred method
from importlib.resources import files

# Assuming data_files is at the root of the 'temoa' package or repo structure
# If installed, 'temoa' package might contain data_files or it might be in share
# For now, let's try to find it relative to 'temoa' package prompt
# If 'temoa' is the top level package:
qt = files('temoa')
source_db_file: str = str(qt.joinpath('data_files', 'untracked_data', db))
except (ImportError, AttributeError):
# Fallback for older python or if files() is not working as expected
# This part is tricky without knowing exact install layout.
# Assuming dev install where this script is in temoa/temoa/utilities
# and data_files is in temoa/data_files
current_file = Path(__file__).resolve()
# temoa/temoa/utilities/capacity_analyzer.py -> temoa/data_files
source_db_file = str(current_file.parents[2] / 'data_files' / 'untracked_data' / db)
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

🧩 Analysis chain

🏁 Script executed:

# Find the capacity_analyzer.py file and show its full path
fd -t f "capacity_analyzer.py" --full-path

# Show the directory structure for temoa
fd -t d "temoa" -x ls -la {} \; 2>/dev/null | head -20

Repository: TemoaProject/temoa

Length of output: 1201


🏁 Script executed:

# Find data_files directory
fd -t d "data_files" --full-path

# Show structure to understand where data_files should be
ls -la | head -20
fd -t d "untracked_data" --full-path

Repository: TemoaProject/temoa

Length of output: 1374


Fix incorrect path comment in fallback fallback logic.

The comment on line 40 is misleading. The actual file path is temoa/utilities/capacity_analyzer.py (not temoa/temoa/utilities/...), and data_files is located at the repository root, not inside temoa/. The .parents[2] calculation is correct and properly reaches the repository root to access data_files/, but the comment should be updated to reflect the actual directory structure: # temoa/utilities/capacity_analyzer.py -> data_files (at repo root).

🤖 Prompt for AI Agents
In @temoa/utilities/capacity_analyzer.py around lines 23 - 41, Update the
misleading fallback comment near the fallback block that builds source_db_file:
clarify that current_file = Path(__file__).resolve() refers to
temoa/utilities/capacity_analyzer.py and that .parents[2] climbs to the
repository root where data_files/untracked_data/db resides (i.e., "#
temoa/utilities/capacity_analyzer.py -> data_files (at repo root)"). Keep the
existing .parents[2] logic and variable names (current_file, source_db_file)
unchanged.

query = f'SELECT tech FROM Technology WHERE flag IN ({in_clause})'

return {row[0] for row in self.cur.execute(query)}
return {cast('str', row[0]) for row in self.cur.execute(query)}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

LGTM, but consider removing quotes around type.

The cast is correct for type narrowing. Since the project requires Python 3.12+, you can simplify to cast(str, row[0]) without the string literal.

🤖 Prompt for AI Agents
In @temoa/utilities/database_util.py at line 133, The cast usage in the return
expression of the method that does "return {cast('str', row[0]) for row in
self.cur.execute(query)}" should use a real type object instead of a string
literal; change the cast call to cast(str, row[0]) (no quotes) in that set
comprehension so it uses the actual type for Python 3.12+ type narrowing while
leaving the rest of the expression (self.cur.execute(query)) unchanged.

Comment on lines +244 to 254
lifetime_process: dict[tuple[Any, ...], float] = {}
data_rows = cur.execute("SELECT region, tech, vintage FROM Efficiency").fetchall()
for rtv in data_rows:
lifetime_process[rtv] = TemoaModel.default_lifetime_tech
data = cur.execute("SELECT region, tech, lifetime FROM LifetimeTech").fetchall()
for rtl in data:
data_rows = cur.execute("SELECT region, tech, lifetime FROM LifetimeTech").fetchall()
for rtl in data_rows:
for v in time_all:
lifetime_process[*rtl[0:2], v] = rtl[2]
data = cur.execute("SELECT region, tech, vintage, lifetime FROM LifetimeProcess").fetchall()
for rtvl in data:
lifetime_process[(*rtl[0:2], v)] = rtl[2]
data_rows = cur.execute("SELECT region, tech, vintage, lifetime FROM LifetimeProcess").fetchall()
for rtvl in data_rows:
lifetime_process[rtvl[0:3]] = rtvl[3]
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider more specific tuple type.

dict[tuple[Any, ...], float] works but could be more precise. Since keys are (region, tech, vintage), consider:

lifetime_process: dict[tuple[str, str, int], float] = {}

Or if vintage types vary, at least dict[tuple[str, str, Any], float].

🤖 Prompt for AI Agents
In @temoa/utilities/db_migration_v3_to_v3_1.py around lines 244 - 254, The
lifetime_process annotation is overly broad; change its type from
dict[tuple[Any, ...], float] to a more specific tuple type reflecting the keys
used (region, tech, vintage). Update the declaration of lifetime_process to use
either dict[tuple[str, str, int], float] if vintage is an int, or
dict[tuple[str, str, Any], float] if vintage can vary, keeping use of
TemoaModel.default_lifetime_tech and the subsequent assignments unchanged.

Comment on lines +283 to 285
if len(df_data) == 0:
print("No data for: " + old_name)
continue
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Prefer df_data.empty for DataFrame emptiness check.

Using len(df_data) == 0 works but df_data.empty is more idiomatic for pandas DataFrames.

-    if len(df_data) == 0:
+    if df_data.empty:
         print("No data for: " + old_name)
         continue
📝 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
if len(df_data) == 0:
print("No data for: " + old_name)
continue
if df_data.empty:
print("No data for: " + old_name)
continue
🤖 Prompt for AI Agents
In @temoa/utilities/db_migration_v3_to_v3_1.py around lines 283 - 285, Replace
the idiomatic DataFrame emptiness check: in the block that currently does "if
len(df_data) == 0: print('No data for: ' + old_name) continue", use the pandas
property "df_data.empty" instead (i.e., "if df_data.empty: ...") so the check is
clearer and more idiomatic while preserving the existing print and continue
behavior; locate the snippet referencing df_data and old_name and make the swap.

Comment on lines 38 to 39
rtv: tuple[str, str, int] = ('A', 'battery', 2020) # rtv
rptv: tuple[str, int, str, int] = ('A', 2020, 'battery', 2020) # rptv
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider using type alias syntax for clarity.

Since this project requires Python 3.12+, you could use PEP 695 type alias syntax for these recurring index types:

type RTV = tuple[str, str, int]
type RPTV = tuple[str, int, str, int]

This would make the intent clearer and allow reuse elsewhere. Based on learnings, PEP 695 type syntax is preferred for type aliases in this project.

🤖 Prompt for AI Agents
In @temoa/utilities/unit_cost_explorer.py around lines 38 - 39, Replace the
inline tuple type annotations for the recurring index tuples with PEP 695 type
aliases: create type RTV = tuple[str, str, int] and type RPTV = tuple[str, int,
str, int], then change the variable annotations for rtv and rptv to use RTV and
RPTV respectively so the intent is clearer and the aliases can be reused across
the module.

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: 4

Caution

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

⚠️ Outside diff range comments (4)
temoa/utilities/sql_migration_v3_1_to_v4.py (1)

29-50: Consider extracting shared mapping logic.

The CUSTOM_MAP, CUSTOM_EXACT_ONLY, CUSTOM_KEYS_SORTED, to_snake_case, map_token_no_cascade, and get_table_info are duplicated between this file and db_migration_v3_1_to_v4.py. Extracting these into a shared module would reduce maintenance burden and ensure consistency.

temoa/utilities/clear_db_outputs.py (1)

39-42: Consider using f-strings for consistency.

The rest of the file uses f-strings (line 34), but these SQL statements use string concatenation. Consider using f-strings for stylistic consistency.

♻️ Suggested change
 for table in basic_output_tables:
-    conn.execute('DELETE FROM ' + table + ' WHERE 1')
+    conn.execute(f'DELETE FROM {table} WHERE 1')
 for table in optional_output_tables:
     try:
-        conn.execute('DELETE FROM ' + table + ' WHERE 1')
+        conn.execute(f'DELETE FROM {table} WHERE 1')
     except sqlite3.OperationalError:
         pass
temoa/utilities/unit_cost_explorer.py (1)

161-168: Typo in variable name: mulitpliermultiplier.

The variable name has a spelling error.

📝 Fix typo
-mulitplier: float = (
+multiplier: float = (
     storage_dur
     * model.segment_fraction_per_season[2020, 'winter']
     * model.days_per_period
     * c2a
     * c
 )
-print(f'The multiplier for the storage should be: {mulitplier}')
+print(f'The multiplier for the storage should be: {multiplier}')
temoa/utilities/db_migration_to_v3.py (1)

138-143: Redundant assignment before continue.

Setting data = [] on line 142 is immediately followed by continue, so the assignment has no effect.

♻️ Remove redundant assignment
     try:
         data: list[Any] = con_old.execute(f'SELECT {cols} FROM {old_name}').fetchall()
     except sqlite3.OperationalError:
         print('TABLE NOT FOUND: ' + old_name)
-        data = []
         continue
🤖 Fix all issues with AI agents
In @temoa/utilities/capacity_analyzer.py:
- Around line 36-37: The current fragile cleanup uses "'con' in locals()" which
is error-prone; update the code that opens the DB
(sqlite3.connect(source_db_file)) and the cursor usage (con.cursor(), cur) to
use a context manager or ensure deterministic cleanup: replace manual locals()
check with "with sqlite3.connect(source_db_file) as con:" and perform cur =
con.cursor() and queries inside that block, handling sqlite3.Error in an except
clause, or if you prefer explicit try/finally ensure con is assigned before
calling con.cursor() and in finally check "if con is not None: con.close()";
reference the sqlite3.connect, con, cur, and sqlite3.Error symbols when making
the change.

In @temoa/utilities/database_util.py:
- Line 195: The code unnecessarily constructs a new tuple with tuple(row) when
rows from self.cur.execute(query) are already tuples; modify the return in the
method that contains self.cur.execute(query) to cast the existing row directly
(e.g., use cast('tuple[str, str]', row) in the set comprehension) instead of
wrapping row in tuple(...), removing the redundant tuple construction.

In @temoa/utilities/unit_cost_explorer.py:
- Around line 145-147: The two construct() calls for
model.v_storage_level.construct() and
model.segment_fraction_per_season.construct() are duplicated; remove the second
set of calls (the repeated model.v_storage_level.construct() and
model.segment_fraction_per_season.construct()) so each construct() is only
invoked once (keep the first occurrences) to avoid potential non-idempotent side
effects.
📜 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 ebc2516 and 7134677.

📒 Files selected for processing (11)
  • pyproject.toml
  • temoa/utilities/capacity_analyzer.py
  • temoa/utilities/clear_db_outputs.py
  • temoa/utilities/database_util.py
  • temoa/utilities/db_migration_to_v3.py
  • temoa/utilities/db_migration_v3_1_to_v4.py
  • temoa/utilities/db_migration_v3_to_v3_1.py
  • temoa/utilities/graph_utils.py
  • temoa/utilities/run_all_v4_migrations.py
  • temoa/utilities/sql_migration_v3_1_to_v4.py
  • temoa/utilities/unit_cost_explorer.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 177
File: temoa/model_checking/commodity_network.py:26-33
Timestamp: 2025-10-27T15:53:41.829Z
Learning: The Temoa project requires Python 3.12 or above as the minimum supported version, so PEP 695 `type` syntax for type aliases is appropriate and preferred over `TypeAlias`.
📚 Learning: 2025-10-27T15:53:41.829Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 177
File: temoa/model_checking/commodity_network.py:26-33
Timestamp: 2025-10-27T15:53:41.829Z
Learning: The Temoa project requires Python 3.12 or above as the minimum supported version, so PEP 695 `type` syntax for type aliases is appropriate and preferred over `TypeAlias`.

Applied to files:

  • temoa/utilities/db_migration_to_v3.py
  • temoa/utilities/unit_cost_explorer.py
🧬 Code graph analysis (4)
temoa/utilities/sql_migration_v3_1_to_v4.py (2)
temoa/utilities/db_migration_v3_1_to_v4.py (1)
  • get_table_info (97-101)
tests/utilities/namespace_mock.py (1)
  • Namespace (1-3)
temoa/utilities/db_migration_v3_1_to_v4.py (2)
temoa/utilities/sql_migration_v3_1_to_v4.py (1)
  • get_table_info (120-124)
tests/utilities/namespace_mock.py (1)
  • Namespace (1-3)
temoa/utilities/database_util.py (1)
temoa/utilities/graphviz_generator.py (1)
  • close (189-194)
temoa/utilities/capacity_analyzer.py (2)
temoa/utilities/graphviz_generator.py (1)
  • connect (173-187)
temoa/utilities/database_util.py (1)
  • close (59-64)
🪛 Ruff (0.14.10)
temoa/utilities/db_migration_to_v3.py

139-139: Possible SQL injection vector through string-based query construction

(S608)

temoa/utilities/db_migration_v3_to_v3_1.py

173-173: Possible SQL injection vector through string-based query construction

(S608)


195-195: Possible SQL injection vector through string-based query construction

(S608)


220-220: Possible SQL injection vector through string-based query construction

(S608)


231-233: Possible SQL injection vector through string-based query construction

(S608)


280-280: Possible SQL injection vector through string-based query construction

(S608)

⏰ 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 (windows-latest, 3.13)
  • GitHub Check: setup and test (macos-latest, 3.13)
  • GitHub Check: setup and test (windows-latest, 3.12)
🔇 Additional comments (23)
pyproject.toml (1)

144-144: LGTM! Enabling type checking for utilities.

The mypy exclude pattern now correctly includes only temoa/extensions/ and stubs/, enabling type checking for the temoa/utilities/ package as intended by this PR.

temoa/utilities/run_all_v4_migrations.py (1)

26-28: LGTM! Accurate generic type annotation.

The return type subprocess.CompletedProcess[str] correctly reflects the use of text=True in the subprocess.run call, ensuring the generic parameter matches the actual output type.

temoa/utilities/sql_migration_v3_1_to_v4.py (3)

27-27: LGTM! Type import added.

The Any import is necessary for the list[tuple[Any, ...]] return type annotation.


120-124: LGTM! Appropriate return type for PRAGMA result.

list[tuple[Any, ...]] correctly represents the variable-length tuple rows returned by PRAGMA table_info.


127-127: LGTM! Proper type annotation for CLI args.

Using argparse.Namespace correctly types the parsed command-line arguments.

temoa/utilities/db_migration_v3_1_to_v4.py (3)

19-19: LGTM! Type import added.

The Any import supports the improved return type annotation.


97-101: LGTM! Consistent return type annotation.

The return type list[tuple[Any, ...]] correctly matches the corresponding function in sql_migration_v3_1_to_v4.py and accurately represents PRAGMA results.


135-135: LGTM! Proper type annotation for CLI args.

Using argparse.Namespace is correct and consistent with the other migration script.

temoa/utilities/clear_db_outputs.py (2)

10-22: LGTM! Type annotations for module-level lists.

The list[str] annotations correctly type the output table name collections.


28-30: LGTM! Type annotations for string variables.

The str annotations are straightforward and accurate for the CLI argument and user input.

temoa/utilities/capacity_analyzer.py (1)

59-62: Type annotation for cumulative_caps initialization is good.

The explicit typing for total_cap: float and cumulative_caps: list[float] improves clarity. The initialization logic correctly handles the first element before iterating.

temoa/utilities/graph_utils.py (2)

40-46: LGTM!

The reformatted TypeVar declaration with trailing commas improves readability and follows modern Python formatting conventions.


49-54: LGTM!

The noqa: UP047 directive is appropriately documented to explain why it's avoiding a runtime type error with NetworkX generics. The function signature is well-typed.

temoa/utilities/database_util.py (2)

61-64: LGTM!

The hasattr guards provide defensive protection against attribute errors if the constructor fails partway through. This aligns with the pattern shown in the relevant code snippet from graphviz_generator.py.


121-123: Good defensive casting.

Explicitly casting to int ensures type consistency regardless of how SQLite returns the data. The comment explains the rationale clearly.

temoa/utilities/db_migration_v3_to_v3_1.py (4)

14-15: Import assumption may need verification.

The comment states "Assumes temoa is installed in the environment." This is a reasonable assumption for a migration script, but it changes from a previous path-based import approach.

Ensure that when this migration script is run, temoa is properly installed in the environment, or document this requirement in the script's usage instructions.


39-41: LGTM!

Explicit type annotations for sqlite3.Connection and sqlite3.Cursor improve code clarity and enable better static analysis.


278-281: Good use of pandas for complex data extraction.

Using pd.read_sql_query simplifies the data loading and provides a cleaner interface for the subsequent DataFrame operations.


173-197: SQL injection note for migration script.

Static analysis flags SQL injection risk due to string-based query construction. While this is a CLI migration tool operating on the user's own databases (low risk), consider using parameterized queries where feasible for defense in depth.

The type annotations and empty-data guards (if not data_transformed: continue) are well implemented.

⛔ Skipped due to learnings
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.
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.
temoa/utilities/unit_cost_explorer.py (1)

14-16: LGTM!

The TYPE_CHECKING block correctly guards the type-only imports to avoid runtime overhead. Based on learnings, PEP 695 style could be used here, but the current approach is also valid.

temoa/utilities/db_migration_to_v3.py (3)

40-42: LGTM!

Explicit type annotations for database connections and cursor improve code clarity and static analysis support.


248-248: Important operator precedence fix.

Adding parentheses around (common ^ techs) is a correctness fix. Without parentheses, not common ^ techs would evaluate as (not common) ^ techs due to Python's operator precedence (not binds tighter than ^). The intended logic is not (common ^ techs) to check if the symmetric difference is empty.


230-234: LGTM!

Good defensive handling - initializing rps_entries to an empty list when the table is not found ensures the subsequent iteration doesn't fail.

Comment on lines 145 to 147
model.v_storage_level.construct()
model.segment_fraction_per_season.construct()

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

Duplicate construct() calls detected.

v_storage_level.construct() and segment_fraction_per_season.construct() are called twice - once at lines 142-143 and again at lines 145-146. This appears to be an unintentional duplication that could cause issues if construct() is not idempotent.

🐛 Remove duplicate calls
-model.v_storage_level.construct()
-model.segment_fraction_per_season.construct()
-
 model.is_seasonal_storage[cast('Technology', 'battery')] = False
📝 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
model.v_storage_level.construct()
model.segment_fraction_per_season.construct()
model.is_seasonal_storage[cast('Technology', 'battery')] = False
🤖 Prompt for AI Agents
In @temoa/utilities/unit_cost_explorer.py around lines 145 - 147, The two
construct() calls for model.v_storage_level.construct() and
model.segment_fraction_per_season.construct() are duplicated; remove the second
set of calls (the repeated model.v_storage_level.construct() and
model.segment_fraction_per_season.construct()) so each construct() is only
invoked once (keep the first occurrences) to avoid potential non-idempotent side
effects.

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/utilities/unit_cost_explorer.py (1)

176-183: Typo: mulitplier should be multiplier.

Proposed fix
-mulitplier: float = (
+multiplier: float = (
     storage_dur
     * model.segment_fraction_per_season[2020, 'winter']
     * model.days_per_period
     * c2a
     * c
 )
-print(f'The multiplier for the storage should be: {mulitplier}')
+print(f'The multiplier for the storage should be: {multiplier}')
🤖 Fix all issues with AI agents
In @temoa/utilities/capacity_analyzer.py:
- Around line 55-58: Guard against an empty or all-zero caps list before
computing normalized cumulative capacities: check if caps is empty or if
total_cap := sum(caps) is 0 and handle by returning an appropriate empty/zero
result (e.g., return [] or cumulative_caps filled with zeros) instead of
performing caps[0] / total_cap; update the code around the variables caps,
total_cap, and cumulative_caps to perform this early-return or conditional logic
and only compute the normalized cumulative list when total_cap > 0.

In @temoa/utilities/unit_cost_explorer.py:
- Around line 163-172: The call to storage_energy_upper_bound_constraint uses
cast('TimeOfDay', '1') passing a string where TimeOfDay values are integers;
change that argument to an integer (e.g., cast('TimeOfDay', 1) or simply 1
depending on your cast helper) so it matches the TimeOfDay set created with
range(1, tod_slices + 1); update the call site near model.is_seasonal_storage
and storage_energy_upper_bound_constraint to pass an integer TimeOfDay value
instead of the string '1'.
📜 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 7134677 and 8c63457.

⛔ Files ignored due to path filters (1)
  • stubs/pyomo/core/base/component.pyi is excluded by !stubs/**
📒 Files selected for processing (11)
  • pyproject.toml
  • temoa/utilities/capacity_analyzer.py
  • temoa/utilities/clear_db_outputs.py
  • temoa/utilities/database_util.py
  • temoa/utilities/db_migration_to_v3.py
  • temoa/utilities/db_migration_v3_1_to_v4.py
  • temoa/utilities/db_migration_v3_to_v3_1.py
  • temoa/utilities/graph_utils.py
  • temoa/utilities/run_all_v4_migrations.py
  • temoa/utilities/sql_migration_v3_1_to_v4.py
  • temoa/utilities/unit_cost_explorer.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-27T15:53:41.829Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 177
File: temoa/model_checking/commodity_network.py:26-33
Timestamp: 2025-10-27T15:53:41.829Z
Learning: The Temoa project requires Python 3.12 or above as the minimum supported version, so PEP 695 `type` syntax for type aliases is appropriate and preferred over `TypeAlias`.

Applied to files:

  • temoa/utilities/db_migration_v3_to_v3_1.py
  • temoa/utilities/unit_cost_explorer.py
🧬 Code graph analysis (3)
temoa/utilities/sql_migration_v3_1_to_v4.py (2)
temoa/utilities/db_migration_v3_1_to_v4.py (1)
  • get_table_info (97-101)
tests/utilities/namespace_mock.py (1)
  • Namespace (1-3)
temoa/utilities/db_migration_v3_to_v3_1.py (1)
temoa/core/model.py (1)
  • TemoaModel (93-1137)
temoa/utilities/unit_cost_explorer.py (3)
temoa/components/costs.py (1)
  • total_cost_rule (522-664)
temoa/components/storage.py (1)
  • storage_energy_upper_bound_constraint (219-273)
temoa/extensions/monte_carlo/mc_run.py (1)
  • model (185-192)
🪛 Ruff (0.14.10)
temoa/utilities/db_migration_v3_to_v3_1.py

173-173: Possible SQL injection vector through string-based query construction

(S608)


195-195: Possible SQL injection vector through string-based query construction

(S608)


220-220: Possible SQL injection vector through string-based query construction

(S608)


231-233: Possible SQL injection vector through string-based query construction

(S608)


280-280: Possible SQL injection vector through string-based query construction

(S608)

temoa/utilities/db_migration_to_v3.py

139-139: Possible SQL injection vector through string-based query construction

(S608)

⏰ 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 (windows-latest, 3.12)
  • GitHub Check: setup and test (macos-latest, 3.13)
  • GitHub Check: setup and test (windows-latest, 3.13)
🔇 Additional comments (37)
pyproject.toml (1)

144-144: LGTM!

Removing temoa/utilities/ from the mypy exclusion list correctly enables type checking for the utilities package, aligning with the typing additions in this PR.

temoa/utilities/unit_cost_explorer.py (4)

6-24: LGTM!

Good use of TYPE_CHECKING guard to avoid runtime import overhead for type-only imports, and from __future__ import annotations enables postponed evaluation of annotations. This is the recommended pattern for the project per the learnings (Python 3.12+).


47-57: LGTM!

The cast() calls correctly narrow string literals to the appropriate NewType aliases for type checking purposes without any runtime overhead.


93-118: LGTM!

The explicit type annotations for local variables (v_new_capacity, v_capacity, total_cost, storage_cap, storage_dur, c2a, c, storage, PJ_to_kwh, price_per_kwh) improve clarity and enable mypy to verify the calculations.


128-146: LGTM!

The type annotations for tod_slices: int and seasonal_fractions: dict[str, float] are correct and consistent with their usage.

temoa/utilities/run_all_v4_migrations.py (1)

26-36: LGTM!

The return type subprocess.CompletedProcess[str] is correct since text=True is passed to subprocess.run, ensuring stdout and stderr are decoded as strings.

temoa/utilities/db_migration_v3_1_to_v4.py (3)

19-19: LGTM!

The Any import is correctly added to support the generic tuple type annotation.


97-101: LGTM!

The return type list[tuple[Any, ...]] accurately reflects that PRAGMA table_info returns rows with varying column types. The empty list return on OperationalError is a sensible fallback.


135-135: LGTM!

Typing the args parameter as argparse.Namespace is correct and consistent with the parse_cli() return type.

temoa/utilities/sql_migration_v3_1_to_v4.py (3)

27-27: LGTM!

The Any import is correctly added to support the generic tuple type annotation, consistent with db_migration_v3_1_to_v4.py.


120-124: LGTM!

The return type list[tuple[Any, ...]] matches the implementation in db_migration_v3_1_to_v4.py, maintaining consistency across the migration utilities.


127-127: LGTM!

Typing args as argparse.Namespace is correct and aligns with the pattern established in the sibling migration file.

temoa/utilities/clear_db_outputs.py (2)

10-22: LGTM!

The type annotations for basic_output_tables and optional_output_tables are correct and use the modern list[str] syntax appropriate for Python 3.12+.


28-30: LGTM!

Type annotations for command-line argument and user input are appropriate.

temoa/utilities/capacity_analyzer.py (2)

24-30: LGTM!

Good use of context manager for database connection, which ensures proper cleanup. The type annotations for res, cur, and iteration are appropriate.


36-43: LGTM!

Type annotations for the capacity processing logic are clear and correctly typed.

temoa/utilities/graph_utils.py (2)

40-46: LGTM!

The multi-line formatting of the GraphType TypeVar in the runtime branch improves readability while maintaining the same type constraints as the TYPE_CHECKING branch.


49-54: LGTM!

The noqa: UP047 directive is appropriate here to avoid runtime type errors with NetworkX generics. The function signature is well-typed with clear parameter and return type annotations.

temoa/utilities/database_util.py (5)

61-64: LGTM!

The hasattr guards are a good defensive pattern to handle cases where __init__ might fail before setting cur or con attributes. This prevents AttributeError during cleanup.


121-123: LGTM!

The explicit cast to int with the explanatory comment is appropriate since SQLite can return different types depending on how data was inserted.


133-133: LGTM!

Using cast('str', row[0]) appropriately narrows the type from the generic cursor result to satisfy the set[str] return type.


179-179: LGTM!

Consistent use of cast for type narrowing, matching the pattern used in get_technologies_for_flags.


195-199: LGTM!

The cast to tuple[str, str] and the broadened return type pd.DataFrame | pd.Series[Any] accurately reflect the runtime behavior where result.sum() returns a Series.

temoa/utilities/db_migration_v3_to_v3_1.py (8)

39-41: LGTM!

Explicit type annotations for the database connections and cursor improve code clarity.


78-111: LGTM!

The typed table mappings (list[tuple[str, str]]) clearly document the structure of the migration configuration.


121-156: LGTM!

Type annotations for operator_added_tables and no_transfer dictionaries are appropriate and self-documenting.


186-197: Verify tuple slicing logic for operator insertion.

The tuple construction (*row[0:op_index], operator, *row[op_index : len(new_cols) - 1]) inserts the operator at the correct position, but the end slice len(new_cols) - 1 assumes the old row has one fewer column than the new schema. This should work correctly given the migration context, but verify that source tables consistently have exactly one less column than the destination.


244-254: LGTM!

The lifetime calculation logic with typed dictionary lifetime_process: dict[tuple[Any, ...], float] is clear. The cascading precedence (default → LifetimeTech → LifetimeProcess) correctly handles lifetime overrides.


279-316: LGTM!

Using pandas DataFrame for complex period calculations is appropriate. The guards for empty data (len(df_data) == 0) and the conditional period assignment based on column presence are well-structured.


373-395: LGTM!

Good defensive pattern: catching OperationalError and initializing data_rows = [] ensures graceful handling when the LoanLifetimeTech table doesn't exist in the source database.


423-430: LGTM!

The FK validation with proper error handling provides useful migration diagnostics.

temoa/utilities/db_migration_to_v3.py (6)

15-15: LGTM!

Type annotations for the SQLite connections and cursor are correct and improve code clarity.

Also applies to: 40-42


54-54: LGTM!

Type annotations for table mappings correctly specify list[tuple[str, str]], accurately reflecting the structure of old-to-new table name pairs.

Also applies to: 109-109, 116-116


130-143: Type annotations are correct; SQL injection warning is a false positive.

The static analysis tool flags line 139 for SQL injection (S608), but this is safe because:

  • Table names (old_name) come from the hardcoded direct_transfer_tables list
  • Column names come from PRAGMA table_info on those tables (trusted schema metadata)
  • SQLite doesn't support parameterized identifiers (table/column names), so string construction is the standard approach for migration scripts

Note: The data = [] assignment on line 142 is immediately followed by continue, making it dead code. It's harmless but could be removed for clarity.


230-234: LGTM!

The type annotation list[tuple[str, str, str]] correctly describes the RPS entry structure (region, tech, notes). Initializing rps_entries = [] in the error path ensures the variable is always defined, which satisfies type checkers even though the guard at line 235 prevents its use when skip_rps is True.


244-253: Good catch: Parentheses fix operator precedence bug.

The original expression not common ^ techs was evaluated as (not common) ^ techs due to Python's operator precedence (not binds tighter than ^), which would incorrectly XOR a boolean with a set.

The corrected not (common ^ techs) properly:

  1. Computes the symmetric difference (mismatches between sets)
  2. Returns True only when there are no mismatches

This is a critical correctness fix.


364-372: LGTM!

The list[Any] annotation is appropriate here since the row tuples contain heterogeneous data types. The null-to-zero conversion for unlim_cap is correctly handled.

Comment on lines +55 to 58
total_cap: float = sum(caps)
cumulative_caps: list[float] = [
caps[0] / total_cap,
]
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

Potential division by zero if caps is empty.

If caps is empty after filtering, total_cap will be 0, causing a ZeroDivisionError on line 57.

Suggested guard
 caps.sort()
 total_cap: float = sum(caps)
+if not caps or total_cap == 0:
+    print('No capacity data to analyze')
+    sys.exit(0)
 cumulative_caps: list[float] = [
     caps[0] / total_cap,
 ]
🤖 Prompt for AI Agents
In @temoa/utilities/capacity_analyzer.py around lines 55 - 58, Guard against an
empty or all-zero caps list before computing normalized cumulative capacities:
check if caps is empty or if total_cap := sum(caps) is 0 and handle by returning
an appropriate empty/zero result (e.g., return [] or cumulative_caps filled with
zeros) instead of performing caps[0] / total_cap; update the code around the
variables caps, total_cap, and cumulative_caps to perform this early-return or
conditional logic and only compute the normalized cumulative list when total_cap
> 0.

Comment on lines +163 to +172
model.is_seasonal_storage[cast('Technology', 'battery')] = False
upper_limit = storage_energy_upper_bound_constraint(
model,
cast('Region', 'A'),
cast('Period', 2020),
cast('Season', 'winter'),
cast('TimeOfDay', '1'),
cast('Technology', 'battery'),
cast('Vintage', 2020),
)
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

Potential type mismatch: TimeOfDay should be an integer, not a string.

On line 129, time_of_day is constructed with range(1, tod_slices + 1) which produces integers. However, line 169 casts '1' (a string) to TimeOfDay. This inconsistency may cause issues when the constraint function indexes into model sets.

Proposed fix
 upper_limit = storage_energy_upper_bound_constraint(
     model,
     cast('Region', 'A'),
     cast('Period', 2020),
     cast('Season', 'winter'),
-    cast('TimeOfDay', '1'),
+    cast('TimeOfDay', 1),
     cast('Technology', 'battery'),
     cast('Vintage', 2020),
 )
📝 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
model.is_seasonal_storage[cast('Technology', 'battery')] = False
upper_limit = storage_energy_upper_bound_constraint(
model,
cast('Region', 'A'),
cast('Period', 2020),
cast('Season', 'winter'),
cast('TimeOfDay', '1'),
cast('Technology', 'battery'),
cast('Vintage', 2020),
)
model.is_seasonal_storage[cast('Technology', 'battery')] = False
upper_limit = storage_energy_upper_bound_constraint(
model,
cast('Region', 'A'),
cast('Period', 2020),
cast('Season', 'winter'),
cast('TimeOfDay', 1),
cast('Technology', 'battery'),
cast('Vintage', 2020),
)
🤖 Prompt for AI Agents
In @temoa/utilities/unit_cost_explorer.py around lines 163 - 172, The call to
storage_energy_upper_bound_constraint uses cast('TimeOfDay', '1') passing a
string where TimeOfDay values are integers; change that argument to an integer
(e.g., cast('TimeOfDay', 1) or simply 1 depending on your cast helper) so it
matches the TimeOfDay set created with range(1, tod_slices + 1); update the call
site near model.is_seasonal_storage and storage_energy_upper_bound_constraint to
pass an integer TimeOfDay value instead of the string '1'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants