Skip to content

feat: Modernize CLI and Refactor Core Sequencer#186

Merged
ParticularlyPythonicBS merged 14 commits intotemoa_davey_codefrom
feat/better_cli
Nov 4, 2025
Merged

feat: Modernize CLI and Refactor Core Sequencer#186
ParticularlyPythonicBS merged 14 commits intotemoa_davey_codefrom
feat/better_cli

Conversation

@ParticularlyPythonicBS
Copy link
Member

@ParticularlyPythonicBS ParticularlyPythonicBS commented Nov 3, 2025

Transforms temoa from script based tool to command line application.
Replaces main.py to a CLI powered by Typer and Rich and refactors temoa sequencer to behave more like a library component.

Description of Changes

  1. CLI Implementation (temoa/cli.py)
  • Replaced main.py with temoa/cli.py: The legacy argparse-based script has been removed. All CLI logic is now self-contained within the temoa package.

  • Configured Installable Entry Point: The pyproject.toml file now includes a [project.scripts] entry, which makes the temoa command available in the user's path after a pip install.

  • Created User-Friendly Commands:

    • temoa run: Mirrors the functionality of the old main.py, accepting a config file and various options (--output, --silent, --debug, etc.).

    • temoa validate: A new, dedicated command that allows users to quickly check if a configuration file and its associated database are valid by building the model without solving it. This is a significant usability improvement.

  • Improved User Interaction: Non-interactive runs are now properly supported via --silent flags, which suppress status messages and INFO-level logs on the console, making the tool scriptable and suitable for CI/CD.

  1. Core Sequencer Refactoring (temoa/_internal/temoa_sequencer.py)

The TemoaSequencer was refactored to decouple it from the application's user interface and control flow. It now behaves as a proper, reusable library component.

  • Removed Program-Terminating Calls: All sys.exit() calls have been replaced with specific exceptions (e.g., RuntimeError, ValueError). The sequencer no longer terminates the application; it reports errors to the caller.

  • Removed User Interaction: All print() and input() statements have been removed. The sequencer is now non-interactive and communicates solely via logging and exceptions.

  • Improved API and Dependency Injection:

    • The init method now accepts a pre-constructed TemoaConfig object instead of a file path. This decouples the sequencer from the configuration loading process and makes it significantly easier to test.

    • The BUILD_ONLY mode has been moved to a dedicated public method, build_model(), clarifying the API contract of the start() method.

  • Bug Fixes & Robustness:

    • A bug was fixed where build_model() would not perform the necessary preliminary checks (e.g., database version). It now runs the same validation as start(), ensuring consistent behavior.
  1. Test Suite Overhaul

The architectural changes required a complete update of the test suite to ensure correctness and validate the new functionality.

  • New CLI Tests (tests/test_cli.py): The obsolete test_main.py has been replaced with a new suite of tests using typer.testing.CliRunner. These tests validate:

  • Updated Unit & Integration Tests: All test files that previously instantiated TemoaSequencer (including conftest.py, test_emission_results.py, test_model.py, etc.) have been updated to use the new API (TemoaSequencer(config=...)).

  • Removed Global State Dependency: The refactoring exposed a hidden dependency on a global OUTPUT_PATH variable. The CLI and test fixtures now explicitly set this value, making the control flow clear and removing side effects.

Summary by CodeRabbit

  • New Features

    • New Typer-based CLI: run/validate commands, --version/--how-to-cite flags, build-only and silent modes, timestamped output folders, richer console and file logging, and a new console script entry point.
  • Refactor

    • Core run/config flow migrated to a config-object API with explicit build vs run paths and clearer mode-driven behavior; legacy CLI entrypoint removed.
  • Tests

    • Tests updated to exercise the CLI and the new config-driven build/run workflows; some legacy tests removed or reworked.
  • Chores

    • Added runtime and dev dependencies to support the CLI and logging; updated ignore rules.

@ParticularlyPythonicBS ParticularlyPythonicBS added Feature Additional functionality refactor labels Nov 3, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 3, 2025

Walkthrough

Replaces the legacy Python CLI entrypoint with a Typer-based CLI, moves CLI/output/logging into temoa/cli.py, refactors TemoaSequencer to accept a TemoaConfig and add build_model(), updates tests to the config-centric API, and expands dependency manifests with typer/rich and transitive packages.

Changes

Cohort / File(s) Change Summary
CLI Framework Migration
main.py, temoa/cli.py
Removed legacy main.py. Added temoa/cli.py implementing a Typer app with run/validate commands, output folder creation, Rich + file logging, version/citation callbacks, and sequencer wiring.
Sequencer Refactor
temoa/_internal/temoa_sequencer.py
Constructor now takes a TemoaConfig; removed path-based init and in-init validations; added build_model(); reorganized start() into mode-driven private handlers and centralized preliminary checks and error handling.
Dependency Manifests
pyproject.toml, requirements.txt, requirements-dev.txt
Added runtime deps typer>=0.20.0, rich>=14.2.0 and related transitive/dev packages; added console script temoa = "temoa.cli:app".
Tests — fixtures & suites updated
tests/conftest.py, tests/test_cli.py, tests/test_emission_results.py, tests/test_material_results.py, tests/test_model.py, tests/test_set_consistency.py, tests/test_temoa_sequencer.py, removed: tests/test_main.py
Tests migrated to use TemoaConfig.build_config(...), instantiate TemoaSequencer(config=...), use build_model() for build-only flows; added CLI tests; removed legacy main argument-parsing tests; adjusted DB handling and some queries.
Repository config
.gitignore
Removed explicit unignore for FEATURE_BRANCH.md, so it is now ignored by default patterns.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as temoa/cli.py
    participant Config as TemoaConfig
    participant Sequencer as TemoaSequencer
    participant DB as Database

    rect rgb(220,230,250)
        Note over CLI: User invokes CLI (run/validate)
        User->>CLI: temoa run/validate config.toml
    end

    rect rgb(230,250,220)
        Note over CLI: CLI prepares environment
        CLI->>CLI: _create_output_folder()
        CLI->>CLI: _setup_logging()
        CLI->>Config: TemoaConfig.build_config(config_file, output_path, silent)
        Config-->>CLI: TemoaConfig
    end

    rect rgb(250,240,220)
        Note over CLI,Sequencer: Sequencer init
        CLI->>Sequencer: TemoaSequencer(config=config, mode_override?)
        Sequencer-->>CLI: Ready
    end

    rect rgb(240,220,250)
        Note over Sequencer: Build or Start
        alt build-only
            CLI->>Sequencer: build_model()
            Sequencer->>Sequencer: _run_preliminary_checks()
            Sequencer->>DB: Load data & construct model
            Sequencer-->>CLI: TemoaModel
        else full run
            CLI->>Sequencer: start()
            Sequencer->>Sequencer: _run_preliminary_checks()
            Sequencer->>Sequencer: match temoa_mode -> _run_*()
            Sequencer->>DB: Solve & persist results
            Sequencer-->>CLI: None (success) / raise on error
        end
    end

    rect rgb(220,250,240)
        CLI->>User: report log path and output folder
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review areas:
    • temoa/cli.py — CLI option semantics, interactive prompts vs. silent behavior, logging setup.
    • temoa/_internal/temoa_sequencer.py — config-driven lifecycle, mode dispatch, exceptions, and correctness of build_model().
    • Tests — ensure migrated tests exercise new APIs and database/output paths correctly.
    • Packaging — confirm console script and added dependencies are appropriate.

Possibly related PRs

Pre-merge checks and finishing touches

✅ 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 summarizes the main changes: modernizing the CLI with Typer/Rich and refactoring the core sequencer from a file-path-based to config-based architecture.
✨ 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 feat/better_cli

📜 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 5da6a5e and 84c1a31.

📒 Files selected for processing (2)
  • .gitignore (0 hunks)
  • temoa/cli.py (1 hunks)
💤 Files with no reviewable changes (1)
  • .gitignore
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-03T22:55:02.437Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 186
File: temoa/_internal/temoa_sequencer.py:60-63
Timestamp: 2025-11-03T22:55:02.437Z
Learning: In `TemoaSequencer.__init__`, when a `mode_override` is provided, it must be written back to `self.config.scenario_mode` (not just stored in `self.temoa_mode`) because `HybridLoader.create_data_dict()` has strict consistency checks that validate `config.scenario_mode` matches the presence/absence of `myopic_index`. Without this synchronization, MYOPIC mode would fail with a RuntimeError when the loader is instantiated.

Applied to files:

  • temoa/cli.py
🧬 Code graph analysis (1)
temoa/cli.py (4)
definitions.py (1)
  • set_OUTPUT_PATH (20-22)
temoa/_internal/temoa_sequencer.py (3)
  • TemoaSequencer (42-250)
  • build_model (80-122)
  • start (124-175)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
temoa/core/modes.py (1)
  • TemoaMode (34-44)
🪛 Ruff (0.14.3)
temoa/cli.py

25-25: datetime.datetime.now() called without a tz argument

(DTZ005)


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

(FBT001)


30-30: Boolean default positional argument in function definition

(FBT002)


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

(FBT001)


30-30: Boolean default positional argument in function definition

(FBT002)


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

(FBT001)


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

(FBT001)


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

(FBT001)


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

(FBT001)


162-162: Boolean default positional argument in function definition

(FBT002)


165-165: Boolean default positional argument in function definition

(FBT002)


210-210: Boolean default positional argument in function definition

(FBT002)


214-214: Boolean default positional argument in function definition

(FBT002)


220-220: Boolean default positional argument in function definition

(FBT002)


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

(FBT001)


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

(FBT001)

🔇 Additional comments (5)
temoa/cli.py (5)

30-95: Excellent logging and sequencer setup architecture.

The logging configuration correctly implements a three-tier verbosity model (debug > normal > silent) with separate console and file handlers. The sequencer setup follows the config-centric pattern established by the refactor, properly initializing the output path, logging, and global state before building the config object.

Key strengths:

  • File logs always capture full detail regardless of console verbosity
  • Library noise (pyomo, matplotlib) is appropriately suppressed
  • Lazy logging format at line 73 (past feedback addressed)
  • Proper dependency injection of TemoaConfig into TemoaSequencer

101-139: Well-implemented callbacks and CLI configuration.

The version and citation callbacks follow the correct Typer pattern for eager option processing. The citation formatting uses Rich's Text API appropriately, and the EN DASH issue from the previous review has been addressed (line 119 now uses a standard hyphen).

The Typer app configuration is sensible: rich markup mode enables better help text, no_args_is_help=True improves UX by showing help when no command is given, and custom help option names support both -h and --help.


145-190: Validate command implementation is correct and well-structured.

The validate command correctly implements non-interactive validation by:

  • Always setting silent=True for the sequencer (line 178) to disable interactive prompts
  • Always overriding to BUILD_ONLY mode (line 180) regardless of config settings
  • Calling build_model() (line 182) which performs all preliminary checks

The distinction between the command's silent flag (user-facing output) and the sequencer's silent flag (interactive behavior) is well-managed. Error handling is appropriate with proper logging and exit codes.


193-257: Run command correctly handles BUILD_ONLY from both CLI flag and config.

The run command properly addresses the previous feedback about respecting configs with scenario_mode set to BUILD_ONLY. Line 239 checks both the --build-only flag and ts.temoa_mode, ensuring that:

  • Configs requesting BUILD_ONLY work without requiring the flag
  • The CLI flag can override other modes to BUILD_ONLY
  • The identity comparison is for enum members is the correct pattern

The silent mode implementation is sound:

  • Interactive confirmation skipped when --silent is set (line 236-238)
  • Success messages suppressed in silent mode (lines 242-244, 248-250)
  • Errors always reported regardless of silent mode

Error handling correctly distinguishes user abort (typer.Abort) from unexpected errors, providing appropriate feedback in each case.


263-285: Global options and entry point are correctly implemented.

The main_options callback correctly uses is_eager=True for early processing of global flags, allowing --version and --how-to-cite to execute before command parsing. The unnecessary pass statement from the previous review has been removed.

The if __name__ == '__main__' block follows the standard pattern for making the module directly executable during development while the package registers temoa.cli:app as the installed console script entry point.


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.

@ParticularlyPythonicBS
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 3, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

📜 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 ca92441 and db3ef37.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .gitignore (0 hunks)
  • main.py (0 hunks)
  • pyproject.toml (2 hunks)
  • requirements-dev.txt (6 hunks)
  • requirements.txt (5 hunks)
  • temoa/_internal/temoa_sequencer.py (1 hunks)
  • temoa/cli.py (1 hunks)
  • tests/conftest.py (3 hunks)
  • tests/test_cli.py (1 hunks)
  • tests/test_emission_results.py (1 hunks)
  • tests/test_main.py (0 hunks)
  • tests/test_material_results.py (3 hunks)
  • tests/test_model.py (1 hunks)
  • tests/test_set_consistency.py (3 hunks)
  • tests/test_temoa_sequencer.py (1 hunks)
💤 Files with no reviewable changes (3)
  • .gitignore
  • main.py
  • tests/test_main.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:

  • requirements-dev.txt
  • pyproject.toml
🧬 Code graph analysis (8)
tests/conftest.py (3)
definitions.py (1)
  • set_OUTPUT_PATH (20-22)
temoa/_internal/temoa_sequencer.py (1)
  • TemoaSequencer (42-237)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
tests/test_material_results.py (2)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
temoa/_internal/temoa_sequencer.py (1)
  • TemoaSequencer (42-237)
temoa/cli.py (4)
definitions.py (1)
  • set_OUTPUT_PATH (20-22)
temoa/_internal/temoa_sequencer.py (3)
  • TemoaSequencer (42-237)
  • build_model (80-109)
  • start (111-162)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
temoa/core/modes.py (1)
  • TemoaMode (34-44)
temoa/_internal/temoa_sequencer.py (4)
temoa/core/config.py (1)
  • TemoaConfig (32-261)
temoa/core/modes.py (1)
  • TemoaMode (34-44)
temoa/_internal/run_actions.py (6)
  • check_python_version (58-68)
  • check_database_version (71-126)
  • build_instance (129-195)
  • solve_instance (212-335)
  • check_solve_status (338-350)
  • handle_results (353-402)
temoa/data_io/hybrid_loader.py (2)
  • HybridLoader (65-904)
  • load_data_portal (122-137)
tests/test_model.py (2)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
temoa/_internal/temoa_sequencer.py (2)
  • TemoaSequencer (42-237)
  • build_model (80-109)
tests/test_emission_results.py (3)
temoa/_internal/temoa_sequencer.py (2)
  • TemoaSequencer (42-237)
  • start (111-162)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
tests/test_material_results.py (1)
  • solved_connection (15-40)
tests/test_set_consistency.py (2)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
temoa/_internal/temoa_sequencer.py (2)
  • TemoaSequencer (42-237)
  • build_model (80-109)
tests/test_temoa_sequencer.py (2)
temoa/_internal/temoa_sequencer.py (3)
  • TemoaSequencer (42-237)
  • start (111-162)
  • build_model (80-109)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
🪛 Ruff (0.14.3)
tests/test_material_results.py

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

(S608)

temoa/cli.py

24-24: datetime.datetime.now() called without a tz argument

(DTZ005)


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

(FBT001)


29-29: Boolean default positional argument in function definition

(FBT002)


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

(FBT001)


29-29: Boolean default positional argument in function definition

(FBT002)


72-72: Logging statement uses f-string

(G004)


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

(FBT001)


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

(FBT001)


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

(FBT001)


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

(FBT001)


148-148: Boolean default positional argument in function definition

(FBT002)


151-151: Boolean default positional argument in function definition

(FBT002)


196-196: Boolean default positional argument in function definition

(FBT002)


200-200: Boolean default positional argument in function definition

(FBT002)


206-206: Boolean default positional argument in function definition

(FBT002)


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

(FBT001)


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

(FBT001)


268-268: Unnecessary pass statement

Remove unnecessary pass

(PIE790)

temoa/_internal/temoa_sequencer.py

63-63: Logging statement uses f-string

(G004)


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

(TRY003)


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

(TRY003)


119-119: Logging statement uses f-string

(G004)


126-128: Avoid specifying long messages outside the exception class

(TRY003)


142-144: Avoid specifying long messages outside the exception class

(TRY003)


205-209: Avoid specifying long messages outside the exception class

(TRY003)


215-217: Avoid specifying long messages outside the exception class

(TRY003)

tests/test_cli.py

116-116: Assertion should be broken down into multiple parts

Break down assertion into multiple parts

(PT018)

tests/test_emission_results.py

25-25: Logging statement uses f-string

(G004)

🔇 Additional comments (4)
tests/conftest.py (1)

79-88: Fixture aligns with config-centric API.

By building the config via TemoaConfig.build_config and seeding OUTPUT_PATH with tmp_path, the fixture now exercises the same surface as the refactored CLI while keeping test artifacts isolated per invocation.

tests/test_temoa_sequencer.py (1)

32-48: Good coverage across modes.

Parameterizing start() with explicit mode_override values ensures the new contract is exercised for every supported path, and asserting the None return keeps the signal crisp.

tests/test_model.py (1)

23-30: Correct use of build_model for BUILD_ONLY.

Switching the serialization test to call build_model() validates the dedicated BUILD_ONLY surface and keeps the regression protection aligned with the sequencer refactor.

tests/test_set_consistency.py (1)

36-42: Set consistency test now mirrors public API.

Constructing the config with build_config() and invoking build_model() keeps this regression harness in lockstep with the sequencer redesign while maintaining the same coverage.

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

📜 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 869fcff and 5da6a5e.

📒 Files selected for processing (1)
  • temoa/cli.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-03T22:55:02.437Z
Learnt from: ParticularlyPythonicBS
Repo: TemoaProject/temoa PR: 186
File: temoa/_internal/temoa_sequencer.py:60-63
Timestamp: 2025-11-03T22:55:02.437Z
Learning: In `TemoaSequencer.__init__`, when a `mode_override` is provided, it must be written back to `self.config.scenario_mode` (not just stored in `self.temoa_mode`) because `HybridLoader.create_data_dict()` has strict consistency checks that validate `config.scenario_mode` matches the presence/absence of `myopic_index`. Without this synchronization, MYOPIC mode would fail with a RuntimeError when the loader is instantiated.

Applied to files:

  • temoa/cli.py
🧬 Code graph analysis (1)
temoa/cli.py (4)
definitions.py (1)
  • set_OUTPUT_PATH (20-22)
temoa/_internal/temoa_sequencer.py (3)
  • TemoaSequencer (42-250)
  • build_model (80-122)
  • start (124-175)
temoa/core/config.py (2)
  • TemoaConfig (32-261)
  • build_config (156-172)
temoa/core/modes.py (1)
  • TemoaMode (34-44)
🪛 Ruff (0.14.3)
temoa/cli.py

25-25: datetime.datetime.now() called without a tz argument

(DTZ005)


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

(FBT001)


30-30: Boolean default positional argument in function definition

(FBT002)


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

(FBT001)


30-30: Boolean default positional argument in function definition

(FBT002)


73-73: Logging statement uses f-string

(G004)


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

(FBT001)


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

(FBT001)


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

(FBT001)


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

(FBT001)


119-119: String contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF001)


162-162: Boolean default positional argument in function definition

(FBT002)


165-165: Boolean default positional argument in function definition

(FBT002)


210-210: Boolean default positional argument in function definition

(FBT002)


214-214: Boolean default positional argument in function definition

(FBT002)


220-220: Boolean default positional argument in function definition

(FBT002)


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

(FBT001)


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

(FBT001)


282-282: Unnecessary pass statement

Remove unnecessary pass

(PIE790)

⏰ 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 (3)
temoa/cli.py (3)

76-95: Well-structured sequencer setup with proper dependency ordering.

The function correctly sequences the initialization steps (output path → logging → global state → config → sequencer) and properly delegates mode synchronization to the TemoaSequencer constructor.


145-190: Validate command correctly enforces build-only validation.

The validate command appropriately forces BUILD_ONLY mode via mode_override, ensuring validation always builds without solving regardless of the config file's scenario_mode. The separation between sequencer silent mode (always True) and user output silent mode (configurable) is well-designed.


193-257: Run command correctly respects BUILD_ONLY from both CLI and config.

Line 239's condition build_only or ts.temoa_mode is TemoaMode.BUILD_ONLY properly addresses the past review concern by checking both the CLI flag and the config file's scenario_mode. This ensures that:

  1. Configs with scenario_mode = BUILD_ONLY work without requiring --build-only
  2. The --build-only flag overrides other modes when specified
  3. Users can validate via temoa run config.toml when config specifies BUILD_ONLY

The error handling gracefully manages user aborts (typer.Abort) and runtime errors, providing clean CLI behavior.

Comment on lines +23 to +27
def _create_output_folder() -> Path:
"""Create a default time-stamped folder for outputs."""
output_path = Path('output_files', datetime.now().strftime('%Y-%m-%d_%H%M%S'))
output_path.mkdir(parents=True, exist_ok=True)
return output_path
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

Specify timezone for timestamp consistency.

The timestamp generated by datetime.now() is timezone-naive, which can cause inconsistencies in distributed environments or when comparing logs across different systems.

Apply this diff to use UTC timezone:

-def _create_output_folder() -> Path:
-    """Create a default time-stamped folder for outputs."""
-    output_path = Path('output_files', datetime.now().strftime('%Y-%m-%d_%H%M%S'))
+def _create_output_folder() -> Path:
+    """Create a default time-stamped folder for outputs."""
+    output_path = Path('output_files', datetime.now(datetime.UTC).strftime('%Y-%m-%d_%H%M%S'))

Note: datetime.UTC requires Python 3.11+. For earlier versions, use datetime.now(timezone.utc) with from datetime import timezone.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.3)

25-25: datetime.datetime.now() called without a tz argument

(DTZ005)

🤖 Prompt for AI Agents
In temoа/cli.py around lines 23 to 27, the code uses datetime.now() which is
timezone-naive; replace it with a UTC-aware timestamp by importing timezone and
using datetime.now(timezone.utc) (or datetime.now(datetime.UTC) on Python 3.11+)
when building the folder name, keep the existing strftime format, and add the
necessary import (from datetime import datetime, timezone) at the top of the
file so the output folder name is generated with a consistent UTC timestamp.

@ParticularlyPythonicBS ParticularlyPythonicBS merged commit e2ccb17 into temoa_davey_code Nov 4, 2025
3 checks passed
@ParticularlyPythonicBS ParticularlyPythonicBS deleted the feat/better_cli branch November 4, 2025 22:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Additional functionality refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant