Skip to content

refactor: one near-miss clause, one fold, and comments that restate their line - #361

Merged
FBumann merged 1 commit into
mainfrom
refactor/dry-and-comment-cleanup
Jul 31, 2026
Merged

refactor: one near-miss clause, one fold, and comments that restate their line#361
FBumann merged 1 commit into
mainfrom
refactor/dry-and-comment-cleanup

Conversation

@FBumann

@FBumann FBumann commented Jul 31, 2026

Copy link
Copy Markdown
Owner

A review pass over the package for duplication and for comments that restate
the code they sit above. No behaviour change — 700 passed, ruff clean,
pyrefly 0 errors.

This is what survived rebasing onto #359. The engine-side half of the same
pass (the row-major label arithmetic written once, plan.children,
dataclasses.replace over TermFragment, the shared acyclic-shift edge
filter) reached main with that branch, as did the doubled _check_where
call it fixed on the way. Only prose conflicted, and I kept main's wording.

DRY

errors.did_you_mean. The "Did you mean …?" clause was spelled three
times — an unknown declaration name, an unknown YAML key, an unknown
symbol-table entry. Each caller keeps its own framing sentence, since that is
the part that actually differs; only the clause is shared. errors.py's
docstring had already observed there were two copies before it added the
third. Drops difflib from schema.py and typeset/symbols.py.

where_parser._folder. _fold_and and _fold_or differed only in the
node class. The fold is the grammar's associativity, which is one rule.

resolution._declared_rhs_error. _resolve_where ran
isinstance(value, str) and ns.kind(value) == ... three times in sequence for
the three things a where-comparison's right-hand side may not name — six lines
above a match ns.kind(...) doing the same job. Now one walrus and a
dispatch. The three messages stay distinct: only the dimension case is a
silent wrong answer (the RHS reads as a literal coordinate and the mask
empties), so its wording has to say more than the other two.

Namespace._unknown built its two kind labels and their two value lists
through parallel ternaries that had to be kept in step by hand; one list of
pairs.

Comments

Removed the ones that restate the line below them: the per-isinstance
branch labels in _coerce_to_dataarray, the # Step 2: / # Step 5:
numbering of a sequence that no longer exists, # Atoms / # Unary /
# Function calls over the grammar rules they name, # Referenced dimensions must be declared.

Every comment carrying an argument stays — the Keyword-not-Literal note,
the float-not-int notes, the whole rationale layer this codebase runs on.
Where a section label was doing real work it was rewritten into one rather
than deleted (# at most one comparison, and only at the top, # NOT binds tightest, then AND, then OR).

Also drops expression_parser.RESOLVED_REFERENCE_NODES — unused, and its
comment claimed "both consumers assert on this" when neither did.

Considered and left alone

typeset/markdown's eleven forwarding methods and data_validation's overlap
with linopy/loader both look like duplication and are both argued for in
their module docstrings — the first against silent inheritance, the second
with tests/test_data_parity.py holding the two lanes to the same verdict.
And walk.glossaries, where only two of the three Entry comprehensions
share a shape; a helper there costs two dict rebuilds to save six lines and
reads worse.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved validation messages for unknown names, keys, and symbols with more consistent “did you mean?” suggestions.
    • Clarified diagnostics when invalid parameters, variables, or dimensions are used in filtering conditions.
    • Preserved existing parsing behavior and operator precedence while improving consistency across error reporting.
  • Refactor

    • Consolidated shared diagnostic formatting to provide more uniform feedback throughout the application.

…heir line

What survived rebasing onto #359 — the engine-side half of this pass (the
label arithmetic, `plan.children`, `dataclasses.replace` over `TermFragment`,
the shared shift-edge filter) landed with that branch, and the duplicated
`_check_where` call it also fixed is upstream too.

DRY:

- The "Did you mean …?" clause was written three times — for an unknown
  declaration name, an unknown YAML key, and an unknown symbol-table entry.
  `errors.did_you_mean` is now the clause and each caller keeps its own
  framing sentence, which is the part that genuinely differs. `errors.py`'s
  own docstring had already noted there were two copies before it added a
  third. Drops the `difflib` import from `schema.py` and `typeset/symbols.py`.
- `_fold_and` and `_fold_or` differed only in the node class; `_folder` takes
  it as an argument. The fold is the grammar's associativity, which is one
  rule.
- `_resolve_where` tested `isinstance(value, str) and ns.kind(value) == ...`
  three times in sequence for the three things a where-comparison's RHS may
  not name. One walrus and a `_declared_rhs_error` dispatch; the three
  messages stay distinct, because only the dimension case is a silent wrong
  answer rather than an obvious one.
- `Namespace._unknown` built its two kind labels and their two value lists
  through parallel ternaries that had to stay in step; one list of pairs.

Comments: removed the ones that restate the line below them — the
per-`isinstance` branch labels in `_coerce_to_dataarray`, `# Step 2:` and
`# Step 5:` numbering a sequence that no longer exists, `# Atoms` / `# Unary`
/ `# Function calls` over the grammar they name, `# Referenced dimensions
must be declared`. Every comment carrying an argument stays, and where a
section label was doing real work it was rewritten into one instead
(`# at most one comparison, and only at the top`, `# NOT binds tightest, then
AND, then OR`).

Also drops `expression_parser.RESOLVED_REFERENCE_NODES`, unused, and whose
comment claimed both consumers asserted on it when neither did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change centralizes unknown-name suggestions, simplifies namespace diagnostics, consolidates where-expression folding, reorganizes grammar definitions, and removes obsolete internal comments. Runtime parsing, validation, coercion, and error behavior remain unchanged except for shared diagnostic construction.

Changes

Diagnostics and parser maintenance

Layer / File(s) Summary
Shared name diagnostics and resolution
src/lpspec/errors.py, src/lpspec/resolution.py, src/lpspec/schema.py, src/lpspec/typeset/symbols.py
did_you_mean now provides shared suggestion formatting. Namespace, schema, and symbol-table diagnostics use centralized logic.
Expression and where parser refactoring
src/lpspec/expression_parser.py, src/lpspec/where_parser.py
Expression grammar organization and where-expression folding were simplified without changing precedence or associativity.
Internal comment cleanup
src/lpspec/linopy/builder.py, src/lpspec/linopy/loader.py
Obsolete comments were removed. Runtime behavior remains unchanged.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 shared near-miss helper, fold consolidation, and redundant comment cleanup.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/dry-and-comment-cleanup

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.

@FBumann
FBumann merged commit 48464fc into main Jul 31, 2026
3 of 4 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lpspec/resolution.py`:
- Around line 361-386: The diagnostics from _declared_rhs_error must not infer
the left-hand operand’s kind from the RHS kind. Update the parameter, variable,
and dimension messages to describe only the invalid RHS reference, or extend the
helper and its _resolve_where callers with the actual left-hand kind; ensure
both parameter-to-dimension and dimension-to-parameter comparisons receive
accurate, neutral wording.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ec7420a-43d1-45a0-8fab-8427976cc673

📥 Commits

Reviewing files that changed from the base of the PR and between f0ad327 and e5180c7.

📒 Files selected for processing (8)
  • src/lpspec/errors.py
  • src/lpspec/expression_parser.py
  • src/lpspec/linopy/builder.py
  • src/lpspec/linopy/loader.py
  • src/lpspec/resolution.py
  • src/lpspec/schema.py
  • src/lpspec/typeset/symbols.py
  • src/lpspec/where_parser.py
💤 Files with no reviewable changes (2)
  • src/lpspec/linopy/builder.py
  • src/lpspec/linopy/loader.py

Comment thread src/lpspec/resolution.py
Comment on lines +361 to +386
def _declared_rhs_error(context: str, node: UnresolvedComparisonNode, value: str, kind: str) -> str:
"""Why the right-hand side of a where-comparison may not name a declaration.

One refusal — the RHS is a literal — but three distinct reasons, and the
wording has to say which, since only the dimension case is a *silent* wrong
answer rather than an obvious one.
"""
shown = f"'{node.name} {node.op} {value}'"
if kind == 'parameter':
return (
f'{context}: {shown} compares two parameters, which is not in the '
f'language — a where-comparison tests one parameter or dimension against '
f'a literal. Precompute the comparison as a boolean parameter in data '
f'prep and test that.'
)
if kind == 'variable':
return f'{context}: {shown} compares against variable {value!r}. A where mask is built before variables exist.'
return (
f'{context}: {shown} compares against dimension {value!r}, which the RHS reads '
f'as the literal coordinate {value!r} — so the predicate tests one dimension '
f"against another dimension's *name* and masks everything out. Comparing two "
f'dimensions to each other is not in the language; if {value!r} is a coordinate '
f'rather than the dimension, rename one of the two.'
)


Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the RHS diagnostic neutral about the left-hand kind.

_declared_rhs_error receives only the RHS kind, but _resolve_where accepts both parameters and dimensions as the left-hand name. Therefore, where: dimension > parameter is reported as “compares two parameters”, while where: parameter > dimension is described as a dimension-to-dimension comparison. Use wording based only on the RHS, or pass the left-hand kind into this helper.

Also applies to: 424-425

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lpspec/resolution.py` around lines 361 - 386, The diagnostics from
_declared_rhs_error must not infer the left-hand operand’s kind from the RHS
kind. Update the parameter, variable, and dimension messages to describe only
the invalid RHS reference, or extend the helper and its _resolve_where callers
with the actual left-hand kind; ensure both parameter-to-dimension and
dimension-to-parameter comparisons receive accurate, neutral wording.

@FBumann
FBumann deleted the refactor/dry-and-comment-cleanup branch July 31, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant