Skip to content

using enums for operator expression for simpler and more robust func…#161

Merged
ParticularlyPythonicBS merged 1 commit intotemoa_davey_codefrom
enum_operator_expression
Oct 10, 2025
Merged

using enums for operator expression for simpler and more robust func…#161
ParticularlyPythonicBS merged 1 commit intotemoa_davey_codefrom
enum_operator_expression

Conversation

@ParticularlyPythonicBS
Copy link
Member

@ParticularlyPythonicBS ParticularlyPythonicBS commented Oct 10, 2025

Using enums for the operator expression utility to make it more robust and harder to make typos with.

Currently wrapping every call with Operator, maybe there is a better place to transform the operators first, but haven't figured out where yet

Summary by CodeRabbit

  • Refactor

    • Replaced string-based operators with a typed operator enum and direct operator-to-expression mapping for more consistent constraint handling.
  • Chores

    • Updated internal usages to rely on the new operator enum; runtime behavior for constraints is unchanged for most users.
  • Note for Integrators

    • One public parameter now has an explicit type annotation; callers may need to supply the typed operator form (enum) instead of raw strings.

@coderabbitai
Copy link

coderabbitai bot commented Oct 10, 2025

Walkthrough

Adds a public Operator Enum and changes operator_expression to require Expression operands and an Operator enum; call sites in limits and storage now construct Operator(op) and pass it to operator_expression, which returns Pyomo comparison expressions or raises ValueError for invalid variants.

Changes

Cohort / File(s) Summary
Utils: new enum and signature
temoa/components/utils.py
Added public Operator Enum (EQUAL, LESS_EQUAL, GREATER_EQUAL). Replaced `operator_expression(lhs: Expression
Limits: operator usage updates
temoa/components/limits.py, temoa/components/...limits_*.py
Updated many constraint builders to import Operator and wrap raw operator values with Operator(op) when calling operator_expression. One signature gained a type annotation: LimitTechInputSplitAverage_Constraint(..., op: str). No other logic changes.
Storage: operator usage update
temoa/components/storage.py
Imported Operator and updated LimitStorageFraction_Constraint to call operator_expression(..., Operator(op), ...). No public signature changes.

Sequence Diagram(s)

sequenceDiagram
    participant Builder as Constraint builder (limits / storage)
    participant Utils as temoa.components.utils

    Builder->>Utils: import Operator
    Builder->>Utils: Operator(op)        %% wrap raw op into Enum
    Builder->>Utils: operator_expression(lhs, Operator, rhs)
    note right of Utils #E8F0FF: match Operator\nreturn lhs == / <= / >= rhs\nor raise ValueError
    Utils-->>Builder: Pyomo comparison Expression
    Builder->>Builder: attach Expression to Constraint
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly describes the primary change to use enums for the operator_expression utility, reflecting the introduction of the Operator enum to improve robustness and simplicity. It succinctly conveys the intent of the pull request without listing specific files or internal details. As such, it is focused and understandable to a teammate scanning history.
Docstring Coverage ✅ Passed Docstring coverage is 95.45% which is sufficient. The required threshold is 80.00%.
✨ 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 enum_operator_expression

📜 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 c50300c and 71933db.

📒 Files selected for processing (3)
  • temoa/components/limits.py (20 hunks)
  • temoa/components/storage.py (2 hunks)
  • temoa/components/utils.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
temoa/components/storage.py (1)
temoa/components/utils.py (2)
  • Operator (23-26)
  • operator_expression (29-37)
temoa/components/limits.py (1)
temoa/components/utils.py (2)
  • Operator (23-26)
  • operator_expression (29-37)
🪛 Ruff (0.13.3)
temoa/components/utils.py

37-37: 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). (1)
  • GitHub Check: setup and test (3.12)
🔇 Additional comments (6)
temoa/components/storage.py (2)

18-18: LGTM! Import correctly includes the new Operator enum.

The import addition aligns with the refactored operator_expression signature that now requires an Operator enum.


490-490: LGTM! Operator enum wrapping is correct and validates input.

The Operator(op) construction converts the string op parameter to an enum member, failing fast with a ValueError if the user provides an invalid operator string. This improves type safety and catches configuration errors early.

temoa/components/utils.py (1)

23-27: LGTM! Well-defined enum with clear semantic names.

The Operator enum inherits from str for compatibility while providing type-safe operator values. The semantic names (EQUAL, LESS_EQUAL, GREATER_EQUAL) are more readable than raw strings.

temoa/components/limits.py (3)

21-21: LGTM! Import correctly includes the new Operator enum.

The import addition enables the enum-based operator handling throughout this constraints module.


262-262: LGTM! All Operator enum constructions are consistent and correct.

All call sites consistently wrap op with Operator(op) before passing to operator_expression. This provides:

  • Type safety at construction time (invalid operator strings raise ValueError)
  • Early validation of user-provided operator values
  • Consistent API usage across all constraint functions

No double-wrapping issues detected (e.g., lines 814/819 pass unwrapped op to helper functions that wrap once at lines 912/914).

Also applies to: 329-329, 369-369, 400-400, 455-455, 514-516, 540-542, 564-567, 596-599, 647-651, 677-680, 707-710, 800-800, 912-912, 914-914, 1021-1021, 1023-1023, 1155-1155, 1157-1157, 1216-1216, 1240-1240, 1263-1263


570-570: LGTM! Type annotation improves clarity.

The explicit op: str annotation documents that this parameter receives a string operator value from the constraint indices before conversion to the Operator enum.


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

📜 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 5b01d31 and 46052b5.

📒 Files selected for processing (1)
  • temoa/components/utils.py (2 hunks)
⏰ 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). (1)
  • GitHub Check: setup and test (3.12)

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

📜 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 46052b5 and 95ae023.

📒 Files selected for processing (1)
  • temoa/components/utils.py (2 hunks)
🧰 Additional context used
🪛 Ruff (0.13.3)
temoa/components/utils.py

37-37: 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). (1)
  • GitHub Check: setup and test (3.12)
🔇 Additional comments (1)
temoa/components/utils.py (1)

23-26: LGTM! Enum provides type safety and prevents typos.

The Operator enum effectively replaces string literals with typed constants, making the code more robust and reducing the risk of typos. Inheriting from str preserves backward compatibility for serialization scenarios.

Consider adding a docstring to the enum to clarify its purpose and the meaning of each operator:

 class Operator(str, Enum):
+    """Comparison operators for building Pyomo constraint expressions.
+    
+    Values are abbreviated for efficient serialization.
+    """
     EQUAL = 'e'
     LESS_EQUAL = 'le'
     GREATER_EQUAL = 'ge'

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/components/utils.py (1)

29-37: Add return type annotation and docstring for better API clarity.

The match-based implementation is clean and the ValueError guard correctly handles invalid operators. However, the function would benefit from explicit documentation.

Apply this diff to add a return type annotation and docstring:

-def operator_expression(lhs: Expression, operator: Operator, rhs: Expression):
+def operator_expression(lhs: Expression, operator: Operator, rhs: Expression) -> Expression:
+    """
+    Build a Pyomo comparison expression from operands and operator.
+
+    Args:
+        lhs: Left-hand side expression
+        operator: Comparison operator (EQUAL, LESS_EQUAL, or GREATER_EQUAL)
+        rhs: Right-hand side expression
+
+    Returns:
+        Pyomo comparison expression (==, <=, or >=)
+
+    Raises:
+        ValueError: If operator is not a valid Operator enum member
+    """
     match operator:
         case Operator.EQUAL:
             return lhs == rhs
         case Operator.LESS_EQUAL:
             return lhs <= rhs
         case Operator.GREATER_EQUAL:
             return lhs >= rhs
     raise ValueError(f'Invalid operator: {operator!r}')

Note: The Ruff hint (TRY003) about exception message length can be safely ignored—the message is appropriately concise.

📜 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 95ae023 and bc345b7.

📒 Files selected for processing (3)
  • temoa/components/limits.py (21 hunks)
  • temoa/components/storage.py (2 hunks)
  • temoa/components/utils.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
temoa/components/storage.py (1)
temoa/components/utils.py (3)
  • Operator (23-26)
  • get_variable_efficiency (40-59)
  • operator_expression (29-37)
temoa/components/limits.py (1)
temoa/components/utils.py (2)
  • Operator (23-26)
  • operator_expression (29-37)
🪛 Ruff (0.13.3)
temoa/components/limits.py

816-816: Boolean positional value in function call

(FBT003)


821-821: Boolean positional value in function call

(FBT003)

temoa/components/utils.py

37-37: 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). (1)
  • GitHub Check: setup and test (3.12)
🔇 Additional comments (5)
temoa/components/utils.py (1)

23-26: LGTM! Enum design is sound.

The Operator enum inheriting from both str and Enum allows seamless string-based construction (Operator(op)) while providing type safety. The three comparison operators cover the expected use cases.

temoa/components/storage.py (2)

18-18: LGTM! Import aligns with the enum-based refactoring.


490-490: LGTM! Operator wrapper correctly converts string to enum.

The Operator(op) construction converts the string parameter to the enum type, enabling type-safe expression building. If op contains an invalid value, this will raise a clear ValueError early.

temoa/components/limits.py (2)

21-21: LGTM! Import aligns with the enum-based refactoring.


572-572: LGTM! Type annotation improves clarity.

Adding the op: str type annotation documents the expected parameter type and improves IDE support.

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/components/utils.py (1)

23-37: LGTM with optional suggestions for documentation.

The Operator enum and refactored operator_expression function are well-implemented. The type safety improvement and the ValueError guard (line 37) correctly address previous review feedback.

Optional enhancements:

  1. Add a return type annotation for better type safety:
-def operator_expression(lhs: Expression, operator: Operator, rhs: Expression):
+def operator_expression(lhs: Expression, operator: Operator, rhs: Expression) -> Expression:
  1. Add a docstring to document the function's purpose and behavior:
 def operator_expression(lhs: Expression, operator: Operator, rhs: Expression):
+    """
+    Build a Pyomo comparison expression from operands and an operator.
+
+    Args:
+        lhs: Left-hand side expression
+        operator: Comparison operator (EQUAL, LESS_EQUAL, or GREATER_EQUAL)
+        rhs: Right-hand side expression
+
+    Returns:
+        Pyomo comparison expression (==, <=, or >=)
+
+    Raises:
+        ValueError: If operator is not a valid Operator enum member
+    """
     match operator:
📜 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 bc345b7 and c50300c.

📒 Files selected for processing (3)
  • temoa/components/limits.py (21 hunks)
  • temoa/components/storage.py (2 hunks)
  • temoa/components/utils.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
temoa/components/storage.py (1)
temoa/components/utils.py (2)
  • Operator (23-26)
  • operator_expression (29-37)
temoa/components/limits.py (1)
temoa/components/utils.py (2)
  • Operator (23-26)
  • operator_expression (29-37)
🪛 Ruff (0.13.3)
temoa/components/utils.py

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

(TRY003)

temoa/components/limits.py

814-814: Boolean positional value in function call

(FBT003)


819-819: Boolean positional value in function call

(FBT003)

🔇 Additional comments (2)
temoa/components/storage.py (1)

490-490: LGTM!

The change correctly adapts to the new Operator enum. The Operator(op) constructor will automatically validate that op is one of the allowed values ('e', 'le', 'ge') and raise a clear ValueError if not.

temoa/components/limits.py (1)

262-262: LGTM!

The Operator enum usage is correct and consistent throughout the file (except for the redundant wrapping issue flagged separately). All call sites to operator_expression properly convert string operators to the Operator enum.

@ParticularlyPythonicBS ParticularlyPythonicBS merged commit 6317ce9 into temoa_davey_code Oct 10, 2025
2 checks passed
@ParticularlyPythonicBS ParticularlyPythonicBS deleted the enum_operator_expression branch January 15, 2026 18:55
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.

1 participant