marianoguerra/pure-py/error does not have a README file

    PurePyError

    pub(all) suberror PurePyError {
    PurePyError(Diagnostic)
    } derive(Eq,
    Debug
    )

    The one error this module raises. Everything that can reject carries a Diagnostic, so a caller never has to match on several error types to decide an exit code.

    Diagnostic

    pub(all) struct Diagnostic {
    kind : Kind
    span :
    Span
    ?
    in_module : String?
    related : Array[(
    Span
    , String)]
    prefix : String?
    } derive(Eq,
    Debug
    )

    A rejection: what, where, in which module, and what else the reader should look at.

    related is this port's addition. The reference records one position; a person reading 'g' captured by previous statement, reassigned here wants to see the statement that captured it too. short never prints it, so the comparison with the reference is unaffected.

    Diagnostic::as_parse_error

    fn Diagnostic::as_parse_error(self : Diagnostic) -> Diagnostic

    The same rejection, worded as the reference words a Python syntax error found while LOADING a module: parse error: <msg>. syntax.py lets ast.parse raise and the message reaches the user through check_program.py's load, which prefixes it.

    Diagnostic::at_path

    fn Diagnostic::at_path(self : Diagnostic, path : String) -> Diagnostic

    The same rejection, reported against a file: the path in front of the message, the position dropped, and the exit code unchanged. This is what check_program.py does to an ill-formed module.

    Diagnostic::attribute_to

    fn Diagnostic::attribute_to(self : Diagnostic, q : String) -> Diagnostic

    Record which module a rejection came from, if it does not already say.

    The innermost module wins: a module that fails while being imported keeps its own name, and the importer's path prefix is what check-program adds.

    Diagnostic::code

    fn Diagnostic::code(self : Diagnostic) -> String?

    A short, stable code to search for.

    Diagnostic::exit_code

    fn Diagnostic::exit_code(self : Diagnostic) -> Int

    The exit code this rejection carries, as implementation-plan.md ยง2.1 and the reference's scripts fix them.

    Diagnostic::help

    fn Diagnostic::help(self : Diagnostic) -> String?

    What a person is likely to want to do about it.

    Only where the message does not already say. A rule that is surprising -- the capture rules, and the two "not a first-class value" ones -- earns a line; "duplicate field name 'x'" does not.

    Diagnostic::message

    fn Diagnostic::message(self : Diagnostic) -> String

    The message, exactly as the reference prints it.

    Diagnostic::new

    fn Diagnostic::new(kind : Kind, span? :
    Span
    , in_module? : String, related? : Array[(
    Span
    , String)]) -> Diagnostic

    Diagnostic::raise_

    fn Diagnostic::raise_(self : Diagnostic) -> PurePyError

    Raise this diagnostic.

    Diagnostic::short

    fn Diagnostic::short(self : Diagnostic, path : String, source? :
    Source
    ) -> String

    path:line:col: message, or path: message when there is no position -- the format syntax.py and check_module.py print.

    The column is in UTF-8 bytes when a source is given, because that is what CPython's col_offset counts and what the reference therefore prints. Without a source the stored code-point column is used, which differs only on a line with a non-ASCII character before the error.

    Diagnostic::stage

    fn Diagnostic::stage(self : Diagnostic) -> Stage

    Diagnostic::to_report

    The ONE place this module names error-report.

    Everything below error produces a Diagnostic, which is this module's own type and carries no rendering. This function is the whole bridge, so that swapping the renderer is a one-function change and so that tools/boundary-check.sh can assert that nothing else links it.

    The rendering adds what the reference does not have and a person wants: a second span for the statement that a message refers to but does not point at, a help line where the fix is not obvious from the message, and a stable code to search for.
    fn Diagnostic::with_related(self : Diagnostic, span :
    Span
    , message : String) -> Diagnostic

    A diagnostic with a second place to look.

    Kind

    pub(all) enum Kind {
    PythonSyntax(msg~ : String)
    Prohibited(msg~ : String)
    NotYetSupported(feature~ : String, issue~ : Int)
    IllFormed(Reason)
    Program(msg~ : String)
    } derive(Eq,
    Debug
    )

    What went wrong, in enough detail to reproduce the reference's message exactly. Every arm's message is compared against the reference's, so a wording change here is a conformance failure and not a matter of taste.

    Reason

    pub(all) enum Reason {
    DuplicateFieldName(name~ : String, cls~ : String)
    UnknownBaseClass(base~ : String)
    InheritedFieldClash(field~ : String, base~ : String)
    DuplicateClassName(name~ : String, in_module~ : String)
    UnassignedVariable(name~ : String)
    CapturedReassignment(name~ : String)
    SelfCaptureAssignment(name~ : String)
    CapturedGeneratorVariable(name~ : String)
    UnreachableStatement
    ConstructorArityMismatch(cls~ : String, expected~ : Int, got~ : Int)
    UnknownConstructorKeyword(cls~ : String, expected_fields~ : Array[String])
    PatternArityMismatch(cls~ : String, expected~ : Int, got~ : Int)
    UnknownClassInPattern(cls~ : String)
    UnknownFieldInPattern(cls~ : String, expected_fields~ : Array[String])
    DuplicatePatternKeyword(cls~ : String)
    DuplicateDictKey(key~ : String)
    NonlinearPattern(index~ : Int)
    UnreachableCase(index~ : Int, subsumed_by~ : Int)
    DuplicateMutualName(name~ : String)
    NonTopLevelImport
    ImportAfterStatement
    SubmoduleNameClash(name~ : String, submodule~ : String)
    SubmoduleNotImported(q~ : String)
    UnassignedMember(x~ : String, q~ : String)
    TopLevelReturn
    EmptyFromImport
    UnknownModule(q~ : String)
    UnknownMember(x~ : String, q~ : String)
    ModuleAsValue(name~ : String)
    OwnDescendantImport(q~ : String, q0~ : String)
    ClassAsValue(name~ : String)
    } derive(Eq,
    Debug
    )

    Why a module is not well formed.

    One arm per class in the reference's reasons.py, carrying the same fields, and message reproduces the Python f-strings character for character: these are the .error.expected files of the conformance suite's static buckets, so the wording is fixed by the oracle and not by taste.

    Reason::code

    fn Reason::code(self : Reason) -> String

    A short, stable code for a reason, for --error-format json and for a person searching: purepy::unassigned-variable.

    Reason::message

    fn Reason::message(self : Reason) -> String

    The message, exactly as reasons.py writes it.

    Stage

    pub(all) enum Stage {
    Syntax
    Prohibited
    NotYetSupported
    IllFormedModule
    IllFormedProgram
    Abort
    Stuck
    } derive(Eq,
    Debug
    )

    Where a rejection came from, which is the same thing as which exit code it carries. The reference has one script per stage; this port has one type.

    ill_formed

    fn ill_formed(reason : Reason, span? :
    Span
    ) -> PurePyError

    Reject a module: exit 3, with the reference's own wording.

    ill_formed_program

    fn ill_formed_program(msg : String) -> PurePyError

    Reject a program: exit 4.

    syntax_error

    fn syntax_error(msg : String, span :
    Span
    ) -> PurePyError

    Raise a Python syntax error at span.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    ยฉ 2026 mooncakes.io