Skip to content

Replace empty module name strings with validated ModuleName type#1151

Merged
joscoh merged 11 commits into
mainfrom
jhx/empty_modulename
May 20, 2026
Merged

Replace empty module name strings with validated ModuleName type#1151
joscoh merged 11 commits into
mainfrom
jhx/empty_modulename

Conversation

@joehendrix
Copy link
Copy Markdown
Contributor

@joehendrix joehendrix commented May 8, 2026

Summary

PythonIdent.pythonModule was a raw String, allowing empty module names to propagate silently through the system and cause downstream failures (see #1143). This PR changes pythonModule to use ModuleName — which now guarantees at least one non-empty component — and adopts it more widely through the PySpec pipeline, eliminating this class of bugs at the type level.

Approach

Rather than adding runtime checks at each use site, ModuleName gains a strengthened invariant via a private constructor enforcing non-empty components. PythonIdent.ofComponent uses decide to verify non-emptiness at compile time for literal module names (used in Decls.lean). Runtime parsing goes through ModuleName.ofString? which returns Option.

Key changes

  • PythonIdent.lean: PythonIdent.pythonModule changed from String to ModuleName. New ofComponent constructor with compile-time proof for single-component literals. ModuleName API extended: back, parent (n), push, append/++, toString (sep)
  • PythonIdent.lean: New ModuleName.ofRelativePath provides a single canonical function for deriving a ModuleName from a .py file path relative to a search root, handling both regular files and __init__.py packages. This eliminates redundant ad-hoc implementations scattered across discoverModules, RelativeImportTest, and findInPath — each of which had slightly different __init__.py handling, creating inconsistency and latent bugs (e.g., pySpecToLaurelCommand silently failed for package modules)
  • Specs.lean: findInPath simplified to return (FilePath × Bool) instead of (FilePath × Array ModuleComponent) — the Bool is isInit, and callers use ModuleOfPath.modulePrefix to derive the package prefix. Deleted unused ofFile function
  • Specs/DDM.lean: Converted fromDDM functions to monadic FromDDM (Except (SourceRange × String)) for proper error propagation instead of panics
  • Specs/ToLaurel.lean: signaturesToLaurel takes ModuleName instead of String prefix. Overload extraction factored into dedicated ExtractM monad (no dependency on ToLaurelContext)
  • PySpecPipeline.lean: buildPySpecLaurel takes Array (ModuleName × String) instead of Array (String × String)
  • Specs.lean: Import resolution uses ModuleName throughout; translateFile requires a ModuleName (no longer optional)
  • Specs/Decls.lean: Builtin idents use ofComponent with compile-time proof

Unrelated cleanup

  • Deleted Strata.Python.containsSubstr helper and replaced all call sites with String.contains (which accepts substring patterns in Lean 4.29)

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@github-actions github-actions Bot added the Python label May 8, 2026
Introduce ModuleName as a validated type (non-empty array of non-empty
components) for PythonIdent.pythonModule, eliminating the root cause of
empty module name bugs. Key changes:

- PythonIdent.mk replaced by PythonIdent.ofComponent with compile-time
  proof that module string is non-empty
- DDM.lean converted to monadic FromDDM for proper error reporting
- signaturesToLaurel takes ModuleName instead of String prefix
- Extract overload logic into dedicated ExtractM monad (independent of
  ToLaurelContext)
- buildPySpecLaurel takes Array (ModuleName × String) instead of
  Array (String × String)
- ModuleName API: ofString?, ofString!, back, parent, push, append,
  ofComponent, toString with configurable separator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@joehendrix joehendrix force-pushed the jhx/empty_modulename branch from bfb75a1 to e89e3f5 Compare May 8, 2026 20:53
joehendrix and others added 2 commits May 8, 2026 22:53
Introduce `ModuleOfPath` and `ofRelativePath` in PythonIdent.lean to
provide a single canonical way to derive a ModuleName from a relative
file path. Use it to simplify `discoverModules`, `RelativeImportTest`,
and `findInPath`. Delete the unused `ofFile` function and replace
`containsSubstr` with `String.contains`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Signature

Add `PythonIdent.toString (sep)` to eliminate repeated manual assembly of
underscore-separated ident strings. Change `TypeSignature.rank` key from
`String` to `ModuleName` for type safety.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@joehendrix joehendrix marked this pull request as ready for review May 9, 2026 01:34
@joehendrix joehendrix requested a review from a team May 9, 2026 01:34
Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer left a comment

Choose a reason for hiding this comment

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

🤖🔍 Well-designed PR that eliminates a class of bugs at the type level. The ModuleName invariant is clean, ofRelativePath consolidates scattered logic, and the FromDDM monad properly replaces panic! with structured errors. A few issues below.

Comment thread Strata/Languages/Python/PythonIdent.lean Outdated
Comment thread Strata/Languages/Python/PythonIdent.lean Outdated
Comment thread Strata/Languages/Python/Specs/DDM.lean Outdated
Comment thread Strata/Languages/Python/Specs/Decls.lean Outdated
Comment thread Strata/Languages/Python/Specs/ToLaurel.lean Outdated
Comment thread Strata/Languages/Python/Specs/ToLaurel.lean
Comment thread Strata/Languages/Python/Specs.lean Outdated
joehendrix and others added 2 commits May 19, 2026 00:54
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer left a comment

Choose a reason for hiding this comment

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

🤖🔍 None of the 7 previous review comments have been addressed — the latest commits all predate the review. Specifically:

  1. PythonIdent.lean:77 — still missing s! prefix in panic! message
  2. PythonIdent.lean:117 — typo Stata still present (should be Strata)
  3. DDM.lean:357 — still missing s! prefix in .throw string
  4. Decls.lean:112SpecType.ofComponent still not renamed back to mk
  5. ToLaurel.lean:201-202 — stale docstring still says "Returns the name unchanged if the prefix is empty"
  6. ToLaurel.lean:10,16 — duplicate OverloadTable import still present
  7. Specs.lean:25since := "FIXME" placeholder still present

- Add missing s! prefix to panic/error string interpolations
- Fix "Stata" typo → "Strata" in ModuleName Repr instance
- Revert accidental SpecType constructor rename (ofComponent → mk)
- Remove stale docstring clause about empty prefix
- Remove duplicate OverloadTable import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer left a comment

Choose a reason for hiding this comment

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

🤖🔍 All previous comments appear addressed — reviewer sign-off still needed.

Copy link
Copy Markdown
Contributor

@tautschnig tautschnig left a comment

Choose a reason for hiding this comment

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

Nothing fundamentally blocking, but a couple of suggestions that would seem like genuine improvements.

Comment thread Strata/Languages/Python/PythonIdent.lean
Comment thread Strata/Languages/Python/PythonIdent.lean
Comment thread Strata/Languages/Python/PythonIdent.lean
Comment thread Strata/Languages/Python/PythonIdent.lean
Comment thread Strata/SimpleAPI.lean Outdated
Comment thread Strata/Languages/Python/PySpecPipeline.lean Outdated
Comment thread Strata/Languages/Python/Specs/ToLaurel.lean Outdated
Comment thread Strata/Languages/Python/Specs.lean Outdated
Comment thread Strata/Languages/Python/PythonIdent.lean
MikaelMayer
MikaelMayer previously approved these changes May 19, 2026
- Push ModuleName through extractFunctionSignatures, eliminating dead isEmpty branch
- Rewrite pushOverloadEntry: dedup branches, use extractError instead of dbg_trace,
  replace .get! with match
- Add ModuleOfPath.modulePrefix? returning Option ModuleName directly
- Restore lost warning in discoverModules on ofRelativePath error
- Add #guard cases for parent, ofString? edge cases, round-trips, back, append

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer left a comment

Choose a reason for hiding this comment

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

🤖🔍 All actionable comments from tautschnig's review are addressed in commit 225b37b:

  • ofRelativePath now uses System.FilePath.components (portability fix)
  • ✅ Warning restored in SimpleAPI.lean (behavior regression fixed)
  • extractFunctionSignatures takes ModuleName directly, dead isEmpty branch removed
  • pushOverloadEntry cleaned up: match replaces .get!, extractError .overloadParamDisagree replaces dbg_trace, branches deduplicated
  • modulePrefix? added and call site simplified in Specs.lean
  • #guard cases added for round-trips, back, append, and parent

Items explicitly declined by author (with justification): Inhabited instance removal, back refactoring, parent rename. These are design choices for the reviewer to accept or push back on.

Reviewer sign-off still needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@joehendrix joehendrix marked this pull request as draft May 19, 2026 17:07
Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer left a comment

Choose a reason for hiding this comment

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

🤖🔍 All previous comments appear addressed — reviewer sign-off still needed.

MikaelMayer
MikaelMayer previously approved these changes May 19, 2026
MikaelMayer

This comment was marked as resolved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@joehendrix joehendrix force-pushed the jhx/empty_modulename branch from 13ece5a to d69ce1b Compare May 19, 2026 20:23
MikaelMayer

This comment was marked as resolved.

MikaelMayer
MikaelMayer previously approved these changes May 19, 2026
joscoh
joscoh previously approved these changes May 19, 2026
@MikaelMayer MikaelMayer added this pull request to the merge queue May 19, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch May 19, 2026
Resolve conflicts with Pipeline refactor (PipelineM, MessageKind,
structured messaging). Adapt ModuleName typing to new pipeline
infrastructure, remove old ExtractM monad in favor of ToLaurelM-based
overload extraction, delete Error.lean (replaced by MessageKind.lean).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer left a comment

Choose a reason for hiding this comment

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

🤖🔍 All previous comments appear addressed — reviewer sign-off still needed.

Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer left a comment

Choose a reason for hiding this comment

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

🤖🔍 All previous comments appear addressed — reviewer sign-off still needed.

@joscoh joscoh added this pull request to the merge queue May 20, 2026
Merged via the queue into main with commit b61880f May 20, 2026
22 checks passed
@joscoh joscoh deleted the jhx/empty_modulename branch May 20, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants