simvx.core.scene_io.syntax

The subset of Python the scene editor can read, and how it refuses the rest.

A scene is an ordinary Python module and the engine runs whatever Python it contains. The editor is the narrower surface: it reads and rewrites a scene through parso, which trails CPython’s grammar, so five constructs load and run correctly and cannot be edited structurally.

Measured against parso 0.8.7, whose 3.13 and 3.14 grammars are byte-identical, so this does not age out with the next Python:

  • match / case (3.10)

  • except* (3.11)

  • type X = ... alias statements (3.12)

  • an f-string quoted with the same character it nests, escaped, or written over several lines (PEP 701, 3.12)

  • unparenthesised except A, B: (PEP 758, 3.14)

PEP 695 generic parameter lists (class Foo[T], def f[T]()) parse, and so does the older f-string nesting that alternates quote characters.

None of the five is hard to avoid inside a scene’s __init__, which is mostly node construction. What matters is that the editor says so: it opens the file, shows it, diffs it and reads it, and refuses the edits it cannot make without naming the construct and the line (:class:UnsupportedSceneSyntaxError).

The classification is not a list of these five. It is read back from what parso could not parse (:func:syntax_issues), so a file broken in any other way is refused on the same terms with the parser’s own message.

Module Contents

Classes

SyntaxIssue

One place parso could not read, and the construct that explains it.

Functions

syntax_issues

Every place parso could not read module, classified, in source order.

refusal

The error for a refusal, naming the first construct and its line.

issue_lines

The line numbers issues covers, for a membership test.

first_unexplained

The first issue no documented construct accounts for, or None.

explain_at

The issue that explains line: the one on it, else the last one above it.

Data

API

simvx.core.scene_io.syntax.__all__

[‘SyntaxIssue’, ‘UnsupportedSceneSyntaxError’, ‘explain_at’, ‘first_unexplained’, ‘issue_lines’, ‘re…

simvx.core.scene_io.syntax.SUBSET_REFERENCE

‘simvx.core.scene_io.syntax’

exception simvx.core.scene_io.syntax.UnsupportedSceneSyntaxError(message: str, issues: list[simvx.core.scene_io.syntax.SyntaxIssue])[source]

Bases: ValueError

The scene holds source the editor’s parser cannot read.

Raised by the structural edits on

Class:

~simvx.core.scene_io.SceneClass – adding, removing or reordering a child – when the lines the edit would rewrite sit inside a construct parso could not parse. The file still loads, still runs and still opens for reading: what is refused is the rewrite, because a removal inside a block the parser flattened emits a file that raises IndentationError.

Attr:

issues carries the parse issues behind the refusal, first one first, for a caller that wants to place a marker rather than show a message.

Initialization

Initialize self. See help(type(self)) for accurate signature.

class __cause__
class __context__
__delattr__()
__dir__()
__eq__()
__format__()
__ge__()
__getattribute__()
__getstate__()
__gt__()
__hash__()
__le__()
__lt__()
__ne__()
__new__()
__reduce__()
__reduce_ex__()
__repr__()
__setattr__()
__setstate__()
__sizeof__()
__str__()
__subclasshook__()
class __suppress_context__
class __traceback__
add_note()
class args
with_traceback()
class simvx.core.scene_io.syntax.SyntaxIssue[source]

Bases: typing.NamedTuple

One place parso could not read, and the construct that explains it.

line: int

None

column: int

None

construct: str | None

None

message: str

None

describe() str[source]

One line naming the construct and where it is, for a refusal.

simvx.core.scene_io.syntax.syntax_issues(module: parso.python.tree.Module) list[simvx.core.scene_io.syntax.SyntaxIssue][source]

Every place parso could not read module, classified, in source order.

Empty for a module that parses, which is the ordinary case and the one this is cheapest on: iter_errors walks the tree once and the classification runs only over the lines it reports.

Requires a module parsed with error_recovery=True; a strict parse raises before there is a tree to ask.

simvx.core.scene_io.syntax.refusal(issues: list[simvx.core.scene_io.syntax.SyntaxIssue], *, subject: str, consequence: str) simvx.core.scene_io.syntax.UnsupportedSceneSyntaxError[source]

The error for a refusal, naming the first construct and its line.

subject is what holds it (a path, "this scene") and consequence says what is being refused, in a whole sentence – the two halves this cannot know.

Source that is simply malformed gets the parser’s own message and none of the subset talk: telling the author of a truncated file that it “loads and runs” would be a falsehood, and pointing them at a list of five constructs theirs is not on would send them looking in the wrong place.

simvx.core.scene_io.syntax.issue_lines(issues: list[simvx.core.scene_io.syntax.SyntaxIssue]) set[int][source]

The line numbers issues covers, for a membership test.

simvx.core.scene_io.syntax.first_unexplained(issues: list[simvx.core.scene_io.syntax.SyntaxIssue]) simvx.core.scene_io.syntax.SyntaxIssue | None[source]

The first issue no documented construct accounts for, or None.

None for a scene that is outside parso’s grammar and inside the format’s documented subset: one construct it cannot read, and then the indentation errors its recovery reports over the block body it lifted out. Such a file opens for reading and refuses its edits.

Anything else – a truncated file, an unclosed bracket – has an issue with no classified construct at or above it, and stays the hard refusal at load that it has always been. There is nothing to show a reader of a file that is simply broken, and no subset rule to point them at.

simvx.core.scene_io.syntax.explain_at(issues: list[simvx.core.scene_io.syntax.SyntaxIssue], line: int) simvx.core.scene_io.syntax.SyntaxIssue | None[source]

The issue that explains line: the one on it, else the last one above it.

A block the parser could not read reports its header (match mode:) and then an indentation error on each line of the body it lifted out, and the header is what a reader needs to be told. So a line inside the block is explained by the nearest issue at or before it, which is the header when the body’s own line carries none.