Quote a Python base that names the class being declared [patch] - #67
Merged
Merged
Conversation
…[patch] Python evaluates a base list as the class statement runs, so an interface written over its own implementer -- IVector0<TSelf, T>, which is how every generated ktsu.Semantics quantity is declared -- names something that does not exist yet and raises NameError on import. The module cannot be loaded at all. The declaration is not what is wrong: six targets write it correctly, and Python is the one that reads a base eagerly. The argument is now quoted, which is what typing takes as a forward reference and resolves once something asks, and is what a typing.Generic consumer writes for this case. Only the arguments are quoted, and only at the outermost level: a class may inherit from a subscripted generic holding a forward reference and may not inherit from a string, and one quoted argument already carries every name inside it. PythonGeneratedSourceImportsTests runs what the generator writes through a real interpreter, which is the Python equivalent of the four compile tests and the only way to catch the class of error where the text looks right and the interpreter disagrees. Reverting the fix fails it with the NameError above. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSQCijNKMyTWktjbys6Yu3
MSTEST0046 on each of the six new assertions: Assert.Contains rather than StringAssert.Contains, which is the form this file already uses for the negative it asserts. The pre-existing StringAssert calls are left alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSQCijNKMyTWktjbys6Yu3
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #64
What was wrong
PythonGeneratorwrote a class's bases verbatim. Python evaluates a base list as theclassstatement runs, so a base written over the class being declared names something that does not exist yet:The module cannot be imported at all. This is the self-type idiom — the interface that hands a method the implementing type,
TSelf Create(T value)rather thanIVector0 Create(T value)— which is how every one of the 212 generatedktsu.Semanticsquantities is declared, and it needs no generics to reach:class Node(Visitor[Node])is the same knot.The fix
The declaration is not what is wrong. The AST says the class implements an interface named over itself, six targets write that correctly, and Python is the one that reads a base eagerly. So the fix is Python's:
A string is what
typingtakes as a forward reference and resolves once something asks, by which time the class exists — the spelling atyping.Genericconsumer writes for exactly this case. (from __future__ import annotationsis the other half of the idea and does not reach here: it defers annotations, and a base is an expression.)Two limits, both load-bearing:
IVector0<Wrapper<Length<T>>, T>comes out asIVector0["Wrapper[Length[T]]", T]. The search for the name is through the arguments rather than at the top alone, since Python evaluates all of it at once either way.Everything else about the base list is untouched: an argument naming anything else is left exactly as it was, which
Python_LeavesABaseArgumentNamingSomethingElseUnquotedpins.Tests
The issue's other half is that nothing ran what this generator wrote — there was no Python equivalent of the four compile tests, and no Python test with a self-referential interface at all.
PythonGeneratedSourceImportsTestscloses that. Python has no compiler and the equivalent question is whether the module loads: a Python file builds its own declarations as it is read, so a class statement the language refuses raises then. The driver is written the way a consumer would be — it supplies the interface and the type variable the generated module expects to find, and runs the module against them throughrunpy, which is how a module is run with names already in its namespace. It asks for both declarations afterwards, so a module that loads but declares nothing fails rather than passing quietly. Inconclusive rather than failing where no interpreter is on the path, matching the existing toolchain tests.Verified by reverting the generator change and re-running:
Python_QuotesTheWholeArgumentWhenTheClassIsNestedInItwas added after that run and fails on a revert for the same reason; the three above are what the revert actually printed.With the fix: 857/857 passing, and
dotnet build -c Releaseover the solution is clean — 0 warnings, 0 errors.CLAUDE.mdgains the decision, next to what the other targets do withInterfaces, andToolchainHarnesssays why Python is asked by an interpreter rather than a compiler.The second commit takes the assertion form Sonar named for the six new assertions (
Assert.Containsrather thanStringAssert.Contains, which is what this file already uses for the negative it asserts); the pre-existing calls are left alone.Not in this change
#63 is the Go half of the same survey and is a different bug in a different generator; it is left alone.
🤖 Generated with Claude Code
https://claude.ai/code/session_01MSQCijNKMyTWktjbys6Yu3