simvx.editor.convert_to_class

Turn a node in the editor into a class of its own, and bind the scene to it.

A scene is Python, and a node’s behaviour is its class, so giving a node behaviour means giving it a class the scene file names. That is what this does, end to end: write the class file, point the scene’s own source at it, save, and reload so the node in front of the user IS an instance of the new class – with whatever the template declared already built, the way it will be every time the game runs.

Two shapes, according to which node was picked:

  • A child. The class is written, the live node’s class is rebound, and the save carries the swap into the construction the file already has (:mod:~simvx.editor.scene_diff repoints it where it stands rather than deleting and re-emitting it, so the author’s arguments, comments and any attribute the statement binds all survive). The import goes in beside it.

  • The scene root. There is no construction to repoint – the root is the class the file defines – so that class is rebased instead: class Arena(Node3D) becomes class Arena(ArenaLogic) where the new class extends the base the file already had. What the file gives the root goes on reaching the old base, because the new class stands between them and the templates written here declare no __init__ of their own. The rebase itself moves one name and nothing else; the save that carries it is an ordinary save of that scene, so a value the file already sets can end up set twice – once where the author set it, and again as a kwarg the save writes.

Anything the file cannot express is refused, and the refusal says what stopped it (:class:ConversionRefused). Most of them are answered before a byte is written: the file says something about its root class that this cannot rewrite without guessing – two bases, a base that is not a plain name, a base the running scene does not agree with – or the scene has never been saved, so there is no source to bind to, or the node is deeper in the tree than a save writes, which is the root and its own children, or the class file would be imported under a name that already means something else in Python. The two that can only be answered by trying – a class file that will not import, and a save that cannot point the scene at the class – put the class file back as it was found before they raise, along with the live node’s class and the path entry the import needed, so a refusal always leaves the process as the user left it.

Module Contents

Classes

Conversion

What one conversion did, once the scene has been saved and reloaded.

Functions

class_files_dir

The project’s [editor] class_files_dir, or src when it has none.

default_class_file

Where a class of this name would go, following the project’s own layout.

templates_for

The template names worth offering for node, most specific first.

convert_node_to_class

Give node a class of its own and bind the scene’s source to it.

Data

API

simvx.editor.convert_to_class.log

‘getLogger(…)’

simvx.editor.convert_to_class.__all__

[‘Conversion’, ‘ConversionRefused’, ‘EMPTY_TEMPLATE’, ‘class_files_dir’, ‘convert_node_to_class’, ‘d…

simvx.editor.convert_to_class.EMPTY_TEMPLATE

‘Empty’

exception simvx.editor.convert_to_class.ConversionRefused(message: str)[source]

Bases: Exception

The conversion did not happen, and this says what stopped it.

Nothing of it is on disk when this is raised: the questions the file’s own text can answer are asked before anything is written, and the two that can only be answered by trying – a class file that will not import, a save that cannot point the scene at the class – put that file back as they found it, the scene never having been written at all. :attr:entry is the same

Class:

~simvx.editor.scene_diff.ReportEntry a save would have used to say so, categorised :data:~simvx.editor.scene_diff.DESTRUCTIVE, because the only way past it is for the user to change something themselves.

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.editor.convert_to_class.Conversion[source]

What one conversion did, once the scene has been saved and reloaded.

class_name: str

None

source_path: pathlib.Path

None

module_path: str | None

None

node: simvx.core.Node | None

None

rebased: bool

None

report: tuple[simvx.editor.scene_diff.ReportEntry, ...]

()

simvx.editor.convert_to_class.class_files_dir(state: simvx.editor.state.State) str[source]

The project’s [editor] class_files_dir, or src when it has none.

simvx.editor.convert_to_class.default_class_file(state: simvx.editor.state.State, class_name: str) pathlib.Path | None[source]

Where a class of this name would go, following the project’s own layout.

<project>/<class_files_dir>/<snake_case>.py, which is where the rest of the editor puts user classes and where its class index looks for them. None for a session with no project open, which is a session with nowhere to put one.

simvx.editor.convert_to_class.templates_for(node: simvx.core.Node) list[str][source]

The template names worth offering for node, most specific first.

A template is written against a base class, and only one whose base the node already descends from produces code that runs: the 2D character template reads is_on_floor and moves a Vec2, which a Node3D has no answer for. :data:EMPTY_TEMPLATE leads the list, so the default stays a class with nothing in it.

simvx.editor.convert_to_class.convert_node_to_class(state: simvx.editor.state.State, node: simvx.core.Node, class_name: str, *, destination: pathlib.Path | None = None, template: str | None = None) simvx.editor.convert_to_class.Conversion[source]

Give node a class of its own and bind the scene’s source to it.

destination is the file the class is written to, defaulting to

Func:

default_class_file. A destination that already exists gains the class at module scope with its imports merged, which is how a class lands inside the scene file itself; anything else is written as a new module.

template names an entry of :data:~simvx.editor.templates.TEMPLATES whose body the class is written with, rendered against the node’s own class rather than the template’s declared base, so a CharacterBody3D converted with the plain 3D template still extends CharacterBody3D. The default (:data:EMPTY_TEMPLATE) writes a class with nothing in it.

The scene is saved through the plan/commit path and then reloaded, so what comes back is what the file says – a node built by running the new class’s __init__, declared children and all. The reload is also why this returns a node rather than mutating the one it was given: the tree in front of the user afterwards is a different one.

That the binding reached the file is checked rather than assumed, and twice: the save is read before it is committed (:func:_would_build_it) and refused, with the class file put back as it was found, when the scene would go on constructing the node as it always did; and the node the reload produced is compared against the class that was written (:func:_is_bound_to), which is the end-to-end answer. A conversion that did not bind never returns quietly.

Raises :class:ConversionRefused for anything the file could not express, with the message naming what stopped it and the project as the user left it: everything written between the class file and the scene’s own save is put back by whatever refuses, and the scene is not written until every question has been answered.