simvx.core.scene_io.scene_file

High-level scene-shaped editing surface for parso-parsed sources.

This is Tier 3b of the scene I/O layer. It composes the lossless parse (:mod:source_tree) and prefix-preserving primitives (:mod:edits) with greenfield emission (:mod:emitter) and structural detection (:mod:detection) into a small public API:

SceneFile  — a parsed Python file with byte-perfect round-trip save.
SceneClass — an editable view of one Node-subclass in the file.
ImportSet  — an editable view of the file's top-level imports.

The editor’s save/load path and the IDE’s refactor tools build on this surface. Lower tiers remain available for callers that need finer control.

Module Contents

Classes

SceneFile

A parsed Python scene file with byte-perfect round-trip save.

SceneClass

An editable view of one Node-subclass class definition inside a scene.

ImportSet

Editable view of the file’s top-level imports.

Data

API

simvx.core.scene_io.scene_file.__all__

[‘ImportSet’, ‘SceneClass’, ‘SceneFile’]

class simvx.core.scene_io.scene_file.SceneFile(source_tree: simvx.core.scene_io.source_tree.SourceTree, *, path: pathlib.Path | None)[source]

A parsed Python scene file with byte-perfect round-trip save.

Holds a parso tree plus the original on-disk text. All edits operate on the parso tree; :meth:save writes tree.get_code(). Round-trip identity is guaranteed when no edits were made.

Initialization

__slots__

(‘_source_tree’, ‘_path’, ‘_imports’, ‘_original_text’)

classmethod load(path: str | pathlib.Path) simvx.core.scene_io.scene_file.SceneFile[source]

Read path and parse it.

Raises :class:FileNotFoundError when the file is absent;

Class:

parso.ParserSyntaxError for malformed Python (we use error_recovery=False for explicit save targets so syntax issues surface immediately rather than silently producing a partial tree).

classmethod from_source(text: str, *, path: pathlib.Path | None = None) simvx.core.scene_io.scene_file.SceneFile[source]

Parse already-loaded source text.

path is recorded for :meth:save and error messages but is not read.

classmethod from_runtime(root: simvx.core.node.Node, *, class_name: str | None = None) simvx.core.scene_io.scene_file.SceneFile[source]

Greenfield: emit source for a live :class:Node tree, then parse it.

The returned :class:SceneFile has no path until :meth:save is called with one.

property path: pathlib.Path | None[source]

Path the file was loaded from / will be saved to, or None.

property source_tree: simvx.core.scene_io.source_tree.SourceTree[source]

Underlying lossless :class:SourceTree.

property imports: simvx.core.scene_io.scene_file.ImportSet[source]

Editable view of the file’s top-level imports.

scene_class() simvx.core.scene_io.scene_file.SceneClass[source]

The single primary Node subclass in the file.

Raises :class:AmbiguousSceneError if the file contains multiple Node subclasses; raises :class:ValueError if it contains none.

all_scene_classes() list[simvx.core.scene_io.scene_file.SceneClass][source]

Every Node subclass defined in the file, in source order.

Used for diagnostics and IDE features. The typical scene has one. Detection mirrors :func:primary_node_class_from_source’s rule (Node base + __init__ or class-body Property descriptors).

insert_top_level_class(name: str, base: str, *, body: str = 'pass', before: str | None = None) simvx.core.scene_io.scene_file.SceneClass[source]

Insert class <name>(<base>): <body> at module scope.

Auto-imports base via :class:ImportSet (defaults to simvx.core). Placement is just before the existing scene class (or before before when given) so the new definition sits between imports and the scene that uses it. Returns the new

Class:

SceneClass view.

Raises :class:ValueError if a top-level class with the same name already exists.

dump() str[source]

Current source as a string.

is_dirty() bool[source]

True iff :meth:dump differs from the original input text.

Used by :class:SceneModule to skip writes for files that were opened but never edited.

save(path: str | pathlib.Path | None = None) pathlib.Path[source]

Write to path (or to self.path if not given).

Returns the path written. Raises :class:ValueError if neither is set. self._path is updated on success so subsequent saves without an argument reuse the last destination.

assert_idempotent() None[source]

Assert that :meth:dump equals the original input text.

Useful for tests that verify no accidental edits leaked into a load/save round-trip.

class simvx.core.scene_io.scene_file.SceneClass(file: simvx.core.scene_io.scene_file.SceneFile, class_node: parso.python.tree.Class)[source]

An editable view of one Node-subclass class definition inside a scene.

Initialization

__slots__

(‘_file’, ‘_class’)

property name: str[source]

Class name (e.g. "Arena").

property node: parso.python.tree.Class[source]

Underlying parso :class:Class node.

has_property(name: str) bool[source]
get_property_default(name: str) str | None[source]

Source text of the Property’s default expression, or None.

Inherited Properties are not visible — use the runtime tree to observe inherited values.

add_property(name: str, default_expr: str) None[source]

Insert name = Property(default_expr) into the class body.

Inserted after existing class-level Property declarations. Auto- imports Property via the file’s :class:ImportSet.

remove_property(name: str) None[source]
set_property_default(name: str, default_expr: str) None[source]
get_root_kwarg(name: str) str | None[source]
set_root_kwarg(name: str, value_expr: str) None[source]

Update or insert a kwarg in the root super().__init__(...) call.

remove_root_kwarg(name: str) None[source]
has_child(var_name: str) bool[source]
child_var_names() list[str][source]

Variable names of all children added via self.add_child(<var>), in source order.

add_child(var_name: str, type_name: str, *, before: str | None = None, after: str | None = None, from_module: str = 'simvx.core', **kwarg_exprs: str) None[source]

Insert a child construction + self.add_child pair into __init__.

Position: appended at the end of the existing child block by default; before= or after= (mutually exclusive) places relative to another child.

Auto-imports type_name from from_module (defaults to simvx.core) when the name is not already imported under any alias. Callers placing user classes should pass from_module=type(node).__module__.

Raises :class:ValueError if var_name already exists in the __init__ body or if both before and after are passed.

Note: when the source is procedural (children built inside loops or conditionals — see :func:has_procedural_construction), the inserted statements are appended at the top level of __init__ and may execute in a surprising order relative to the procedural code.

remove_child(var_name: str) None[source]

Remove the assignment line and the self.add_child line.

Does not auto-clean unused imports — that is the caller’s responsibility (use :meth:ImportSet.remove). Raises

Class:

ValueError if var_name is absent.

rename_child(old: str, new: str) None[source]
get_child_kwarg(var_name: str, kwarg: str) str | None[source]
set_child_kwarg(var_name: str, kwarg: str, value_expr: str) None[source]
remove_child_kwarg(var_name: str, kwarg: str) None[source]
reorder_children(order: list[str]) None[source]

Reorder child assignment + add_child line pairs to match order.

Every existing child must appear in order exactly once.

class simvx.core.scene_io.scene_file.ImportSet(source_tree: simvx.core.scene_io.source_tree.SourceTree)[source]

Editable view of the file’s top-level imports.

Initialization

__slots__

(‘_source_tree’,)

has(name: str, *, from_: str | None = None) bool[source]
has_any_alias(name: str) bool[source]

True iff name is imported from anywhere (any module).

ensure(name: str, *, from_: str | None = None) None[source]

Add import name or from <from_> import name if absent.

When from_ matches an existing from <from_> import line, the new name is merged into that line (sorted, deduplicated) instead of creating a separate import line.

remove(name: str, *, from_: str | None = None) None[source]

Remove an import. If the line becomes empty, remove it.

No-op if name is not imported (with the given from_).

names() list[tuple[str | None, str]][source]

List of (from_, name) pairs in source order.