Skip to content

Fix Homebrew install crash from namelist parser#1154

Merged
sbryngelson merged 1 commit intoMFlowCode:masterfrom
sbryngelson:brewfix2
Feb 17, 2026
Merged

Fix Homebrew install crash from namelist parser#1154
sbryngelson merged 1 commit intoMFlowCode:masterfrom
sbryngelson:brewfix2

Conversation

@sbryngelson
Copy link
Member

@sbryngelson sbryngelson commented Feb 17, 2026

User description

Summary

  • The namelist parser (added post-v5.2.0) reads src/*/m_start_up.fpp at runtime to determine valid parameters per target. Homebrew installs don't include these Fortran source files, causing a FileNotFoundError crash when running any case.
  • When sources are unavailable, fall back to a built-in copy of the parsed parameter sets (generated from the same Fortran namelists). This maintains correct per-target filtering while working without the source tree.
  • Verified via Homebrew CI: all 3 macOS targets (14, 15, 26) pass build + Sod shock tube test: https://github.com/MFlowCode/homebrew-mfc/actions/runs/22085224543

Test plan

  • Homebrew CI passes on macos-14, macos-15, macos-26
  • Fallback params match live Fortran parse (verified locally)
  • Dev mode (with sources) still filters correctly (bogus_param rejected)
  • run_time_info correctly excluded from pre_process in fallback

🤖 Generated with Claude Code


CodeAnt-AI Description

Avoid crash when Fortran sources are missing by using built-in namelist parameters

What Changed

  • If the Fortran namelist source files are absent (e.g. Homebrew installs), the parser no longer raises FileNotFoundError and instead uses a built-in set of valid parameters for pre_process, simulation, and post_process
  • Parameter validation continues to be per-target using those fallback sets, so invalid parameters are still rejected even without the source tree
  • No change to behavior when source files are present — live parsing is still used

Impact

✅ No runtime crash on Homebrew installs
✅ Correct per-target parameter validation without source files
✅ Homebrew CI succeeds for macOS targets

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced system robustness with automatic fallback parameter handling. When required source files are unavailable, the system now gracefully switches to built-in default parameters instead of failing, ensuring processes can continue execution with sensible baseline configurations.
  • Documentation

    • Minor code documentation and formatting updates.

…g Fortran sources

The namelist parser (added post-v5.2.0) reads src/*/m_start_up.fpp at runtime
to determine valid parameters per target. Homebrew installs don't include these
Fortran source files, causing a FileNotFoundError crash. When sources are
unavailable, fall back to a built-in copy of the parsed parameter sets (generated
from the same Fortran namelists). This maintains correct per-target filtering
while working without the source tree.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 17, 2026 04:02
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 17, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

📝 Walkthrough

Walkthrough

The pull request introduces a built-in fallback parameter system in the namelist parser to gracefully handle missing Fortran source files for pre_process, simulation, and post_process targets. When required source files are unavailable, parse_all_namelists now returns the fallback parameter dictionary instead of raising an exception. A minor docstring formatting adjustment is also included.

Changes

Cohort / File(s) Summary
Fallback Parameter System
toolchain/mfc/params/namelist_parser.py
Introduces _FALLBACK_PARAMS dictionaries for each target and modifies parse_all_namelists to return fallback parameters instead of raising FileNotFoundError when Fortran source files are missing. Includes updated documentation reflecting this fallback behavior.
Documentation Formatting
toolchain/mfc/run/case_dicts.py
Removes empty line in _is_param_valid_for_target docstring, shifting the Args section up by one line.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

Review effort 3/5, size:M

Poem

🐰 When sources vanish without a trace,
Our fallback params save the day with grace,
No errors thrown, just defaults so keen,
The robustest parsing ever seen! ✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main change: fixing a Homebrew install crash caused by the namelist parser missing Fortran source files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master
Description check ✅ Passed The pull request description effectively summarizes changes, motivation, and testing approach, though it deviates from the template structure by omitting standard sections.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@codeant-ai codeant-ai bot added the size:M This PR changes 30-99 lines, ignoring generated files label Feb 17, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 17, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Fallback scope
    The code returns the built-in fallback parameter sets for all targets as soon as any single target's source file is missing.
    This causes live parsing for remaining targets to be skipped even when their source files are present. Consider falling back
    per-target to preserve live parsing when some source files are available.

  • Mutable fallback sets
    The implementation returns or caches references to the sets in _FALLBACK_PARAMS directly. These are mutable objects and
    may be mutated via downstream code (through the cache), unintentionally changing the module-level fallback constant.
    Return copies to avoid shared mutable state between the fallback constant and the cached parsed results.

  • Preprocessor/comment regex
    The parser removes preprocessor lines using r'#:.*', but common preprocessor directives are typically #if, #ifdef, #endif, etc.
    The current regex may miss such lines (leaving directives in the namelist) or be too specific. Also the comment-removal regex !.*
    can remove content inside strings if not careful. Verify these regexes against representative source files.

Comment on lines +173 to +174
# Use built-in fallback parameters.
return dict(_FALLBACK_PARAMS)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Only use the built-in fallback parameters when all Fortran source files are missing and otherwise raise an explicit error to avoid silently masking partial misconfigurations. [custom_rule]

Severity Level: Minor ⚠️

Suggested change
# Use built-in fallback parameters.
return dict(_FALLBACK_PARAMS)
# Use built-in fallback parameters when all Fortran sources are missing.
if all(not p.exists() for p in targets.values()):
return dict(_FALLBACK_PARAMS)
raise FileNotFoundError(f"Fortran source not found: {filepath}")
Why it matters? ⭐

The current code returns the built-in fallback as soon as any single target file is missing, which can silently mask partial repository misconfigurations (e.g., one target's sources missing while others are present). The proposed change tightens the behavior so the fallback is only used when all Fortran source files are absent, and otherwise raises FileNotFoundError for the specific missing file. This directly improves correctness and aligns with the repository intent documented in the module (fallback only when Fortran sources are unavailable), avoiding partial-missing-source surprises that could lead to inconsistent target parameter sets.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** toolchain/mfc/params/namelist_parser.py
**Line:** 173:174
**Comment:**
	*Custom Rule: Only use the built-in fallback parameters when all Fortran source files are missing and otherwise raise an explicit error to avoid silently masking partial misconfigurations.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 17, 2026

CodeAnt AI finished reviewing your PR.

@sbryngelson sbryngelson merged commit c5493c1 into MFlowCode:master Feb 17, 2026
35 of 41 checks passed
Copy link
Contributor

@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

🧹 Nitpick comments (2)
toolchain/mfc/params/namelist_parser.py (2)

170-175: Early return on first missing file applies fallback to all targets.

If any single source file is missing, the function returns the entire fallback dict without attempting to parse the remaining files. This is fine for the Homebrew case (all sources absent), but if only one file is missing due to corruption or partial install, the user gets no warning and silently loses live-parsed params for the targets whose sources are present.

Consider logging a warning so the fallback isn't completely silent, e.g.:

Suggested improvement
+import logging
+
+log = logging.getLogger(__name__)
+
 ...
     for target_name, filepath in targets.items():
         if not filepath.exists():
-            # Source files not available (e.g. Homebrew install).
-            # Use built-in fallback parameters.
-            return dict(_FALLBACK_PARAMS)
+            # Source files not available (e.g. Homebrew install).
+            # Use built-in fallback parameters.
+            log.debug("Fortran source %s not found; using built-in fallback parameters", filepath)
+            return dict(_FALLBACK_PARAMS)
         result[target_name] = parse_namelist_from_file(filepath)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@toolchain/mfc/params/namelist_parser.py` around lines 170 - 175, The loop in
parse_namelist_from_file caller currently returns the entire _FALLBACK_PARAMS on
the first missing filepath, losing parsed results for other targets; change it
to log a warning (e.g., using logging.warning or warnings.warn) mentioning the
missing filepath and target_name, assign a per-target fallback into result (use
_FALLBACK_PARAMS.get(target_name, dict(_FALLBACK_PARAMS)) or similar) and
continue the loop so remaining targets are parsed with parse_namelist_from_file;
remove the early return and ensure result is populated per-target and returned
after the loop.

174-174: Shallow copy shares mutable set references with _FALLBACK_PARAMS.

dict(_FALLBACK_PARAMS) copies the dict but the Set[str] values are shared objects. If any future code path adds/removes from the returned sets (or from _TARGET_PARAMS_CACHE), it silently mutates the module-level constant. Current usage is read-only so this is safe today, but a deep copy would be more defensive:

-            return dict(_FALLBACK_PARAMS)
+            return {k: set(v) for k, v in _FALLBACK_PARAMS.items()}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@toolchain/mfc/params/namelist_parser.py` at line 174, The current return
dict(_FALLBACK_PARAMS) returns a shallow copy that still shares the Set[str]
values; change it to return a deep copy of the sets so callers cannot mutate the
module-level constants. Replace the shallow copy with either return {k: set(v)
for k, v in _FALLBACK_PARAMS.items()} or use copy.deepcopy(_FALLBACK_PARAMS)
(importing copy), ensuring you update the return near the dict(_FALLBACK_PARAMS)
site and keep references to _FALLBACK_PARAMS and _TARGET_PARAMS_CACHE
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@toolchain/mfc/params/namelist_parser.py`:
- Around line 17-19: The comment "To regenerate: python3
toolchain/mfc/params/namelist_parser.py" is misleading because the __main__
block only prints a summary and does not emit the _FALLBACK_PARAMS dict; either
change the comment to accurately describe the actual manual regeneration steps
or modify the script so its __main__ prints a copy-pasteable _FALLBACK_PARAMS
literal (or writes it to stdout/file). Also add a CI check (a small test/step)
that runs the namelist_parser to parse the live Fortran namelists and compares
the resulting parameter set to the _FALLBACK_PARAMS dict, failing the build if
any keys/values diverge so Homebrew and other consumers notice stale fallbacks.
Ensure references to _FALLBACK_PARAMS and the __main__ summary routine are used
when implementing the print-or-compare behavior.

---

Nitpick comments:
In `@toolchain/mfc/params/namelist_parser.py`:
- Around line 170-175: The loop in parse_namelist_from_file caller currently
returns the entire _FALLBACK_PARAMS on the first missing filepath, losing parsed
results for other targets; change it to log a warning (e.g., using
logging.warning or warnings.warn) mentioning the missing filepath and
target_name, assign a per-target fallback into result (use
_FALLBACK_PARAMS.get(target_name, dict(_FALLBACK_PARAMS)) or similar) and
continue the loop so remaining targets are parsed with parse_namelist_from_file;
remove the early return and ensure result is populated per-target and returned
after the loop.
- Line 174: The current return dict(_FALLBACK_PARAMS) returns a shallow copy
that still shares the Set[str] values; change it to return a deep copy of the
sets so callers cannot mutate the module-level constants. Replace the shallow
copy with either return {k: set(v) for k, v in _FALLBACK_PARAMS.items()} or use
copy.deepcopy(_FALLBACK_PARAMS) (importing copy), ensuring you update the return
near the dict(_FALLBACK_PARAMS) site and keep references to _FALLBACK_PARAMS and
_TARGET_PARAMS_CACHE consistent.

Comment on lines +17 to +19
# Fallback parameters for when Fortran source files are not available.
# Generated from the namelist definitions in src/*/m_start_up.fpp.
# To regenerate: python3 toolchain/mfc/params/namelist_parser.py
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Regeneration comment is misleading; consider adding an automated staleness check.

Line 19 says # To regenerate: python3 toolchain/mfc/params/namelist_parser.py, but the __main__ block (lines 229–261) only prints a summary — it doesn't emit the _FALLBACK_PARAMS dict literal. Either update the script to actually print a copy-pasteable dict, or change this comment to reflect the real process.

Additionally, when a new parameter is added to the Fortran namelists, this fallback dict must also be updated or Homebrew users will silently lose access to that parameter. Consider adding a CI check that parses the live Fortran sources and compares against _FALLBACK_PARAMS, failing if they diverge.

Based on learnings: "When adding a new parameter to toolchain/mfc/params/definitions.py, update the namelist /user_inputs/ in the corresponding src/*/m_start_up.fpp" — _FALLBACK_PARAMS is now a third location that must stay in sync.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@toolchain/mfc/params/namelist_parser.py` around lines 17 - 19, The comment
"To regenerate: python3 toolchain/mfc/params/namelist_parser.py" is misleading
because the __main__ block only prints a summary and does not emit the
_FALLBACK_PARAMS dict; either change the comment to accurately describe the
actual manual regeneration steps or modify the script so its __main__ prints a
copy-pasteable _FALLBACK_PARAMS literal (or writes it to stdout/file). Also add
a CI check (a small test/step) that runs the namelist_parser to parse the live
Fortran namelists and compares the resulting parameter set to the
_FALLBACK_PARAMS dict, failing the build if any keys/values diverge so Homebrew
and other consumers notice stale fallbacks. Ensure references to
_FALLBACK_PARAMS and the __main__ summary routine are used when implementing the
print-or-compare behavior.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a crash in Homebrew installations where Fortran source files are not included in the package. The namelist parser (introduced post-v5.2.0) attempts to read these source files at runtime to validate parameters per target, but when sources are unavailable, it throws a FileNotFoundError.

Changes:

  • Added a built-in fallback parameter set for all three targets (pre_process, simulation, post_process) extracted from the current Fortran namelist definitions
  • Modified parse_all_namelists() to return the fallback when source files are not available instead of raising an error
  • Removed an extraneous blank line in a docstring in case_dicts.py

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
toolchain/mfc/params/namelist_parser.py Added _FALLBACK_PARAMS dict with hardcoded parameter sets and modified parse_all_namelists() to use fallback when source files are missing
toolchain/mfc/run/case_dicts.py Removed blank line in docstring (formatting)

Comment on lines 170 to +174
for target_name, filepath in targets.items():
if not filepath.exists():
raise FileNotFoundError(f"Fortran source not found: {filepath}")
# Source files not available (e.g. Homebrew install).
# Use built-in fallback parameters.
return dict(_FALLBACK_PARAMS)
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The early return on the first missing file assumes that if any source file is missing, all are missing (Homebrew install scenario). However, this could mask a corrupted or partial installation where only some files are missing. Consider checking all three files first and either using fallback for all (if all are missing) or raising an error (if only some are missing). This would make debugging installation issues easier.

Copilot uses AI. Check for mistakes.
@sbryngelson sbryngelson deleted the brewfix2 branch February 17, 2026 04:07
@codecov
Copy link

codecov bot commented Feb 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 44.07%. Comparing base (598df2c) to head (b815a44).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1154   +/-   ##
=======================================
  Coverage   44.07%   44.07%           
=======================================
  Files          70       70           
  Lines       20431    20431           
  Branches     1974     1974           
=======================================
  Hits         9004     9004           
  Misses      10291    10291           
  Partials     1136     1136           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

size:M This PR changes 30-99 lines, ignoring generated files

Development

Successfully merging this pull request may close these issues.

1 participant

Comments